Make a clean environment
rm(list=ls())
Load packages
packages.list <- c("ggplot2","treeio","ggtree","ggnewscale","ape","dplyr","tidyverse","tidyr","phytools","RColorBrewer","lubridate","readxl","ggforce","ggstance","ggridges","cowplot","hexbin","scales","haven","network","ggnetwork","intergraph","igraph","ggraph","graphlayouts","scatterpie","maps","mapdata","maptools","rgdal","rgeos","broom","ggrepel","ggridges","magick","ggbeeswarm","ggrastr")
#"plyr","Cairo","ggmap","emojifont","rPinecone","pairsnp","CoordinateCleaner","gridExtra","dendextend","ggdendro",
#BiocManager::install("ggtree")
#BiocManager::install("treeio")
for(pkg in packages.list){
eval(bquote(library(.(pkg)))) }
Confirm current environmental setup
R.Version()
$platform
[1] "x86_64-apple-darwin17.0"
$arch
[1] "x86_64"
$os
[1] "darwin17.0"
$system
[1] "x86_64, darwin17.0"
$status
[1] ""
$major
[1] "4"
$minor
[1] "1.2"
$year
[1] "2021"
$month
[1] "11"
$day
[1] "01"
$`svn rev`
[1] "81115"
$language
[1] "R"
$version.string
[1] "R version 4.1.2 (2021-11-01)"
$nickname
[1] "Bird Hippie"
print(sessionInfo())
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Monterey 12.6.1
Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib
locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] gtable_0.3.1 randomcoloR_1.1.0.1 fastbaps_1.0.6 rPinecone_0.1.0 devtools_2.4.4
[6] usethis_2.1.6 ggrastr_1.0.1 ggbeeswarm_0.6.0 magick_2.7.3 ggrepel_0.9.1
[11] broom_1.0.0 rgeos_0.5-9 rgdal_1.5-30 maptools_1.1-4 sp_1.4-6
[16] mapdata_2.3.0 scatterpie_0.1.7 graphlayouts_0.7.2 ggraph_2.0.5 igraph_1.3.5
[21] intergraph_2.0-2 ggnetwork_0.5.10 network_1.17.1 haven_2.4.3 scales_1.2.1
[26] hexbin_1.28.2 ggridges_0.5.3 lubridate_1.8.0 RColorBrewer_1.1-3 ggnewscale_0.4.5
[31] cowplot_1.1.1 treemapify_2.5.5 treeio_1.18.1 ggtree_3.2.1 phytools_0.7-90
[36] maps_3.4.0 ape_5.6-2 readxl_1.3.1 ggforce_0.3.3 ggstance_0.3.5
[41] forcats_0.5.1 stringr_1.4.1 dplyr_1.0.7 purrr_0.3.4 readr_2.1.3
[46] tidyr_1.1.4 tibble_3.1.8 ggplot2_3.3.6 tidyverse_1.3.2 vcfR_1.13.0
loaded via a namespace (and not attached):
[1] utf8_1.2.2 clipr_0.8.0 tidyselect_1.2.0 htmlwidgets_1.5.4
[5] grid_4.1.2 combinat_0.0-8 Rtsne_0.16 munsell_0.5.0
[9] codetools_0.2-18 ragg_1.2.2 miniUI_0.1.1.1 withr_2.5.0
[13] colorspace_2.0-3 knitr_1.40 rstudioapi_0.13 stats4_4.1.2
[17] labeling_0.4.2 RgoogleMaps_1.4.5.3 mnormt_2.0.2 polyclip_1.10-0
[21] farver_2.1.1 coda_0.19-4 vctrs_0.5.0 generics_0.1.1
[25] clusterGeneration_1.3.7 xfun_0.34 R6_2.5.1 bitops_1.0-7
[29] cachem_1.0.6 gridGraphics_0.5-1 assertthat_0.2.1 promises_1.2.0.1
[33] pinfsc50_1.2.0 googlesheets4_1.0.0 beeswarm_0.4.0 Cairo_1.5-12.2
[37] processx_3.7.0 phangorn_2.8.0 tidygraph_1.2.0 rlang_1.0.6
[41] systemfonts_1.0.4 scatterplot3d_0.3-41 RcppRoll_0.3.0 splines_4.1.2
[45] lazyeval_0.2.2 gargle_1.2.0 yaml_2.3.6 reshape2_1.4.4
[49] modelr_0.1.8 backports_1.4.0 httpuv_1.6.6 tools_4.1.2
[53] ggplotify_0.1.0 statnet.common_4.5.0 ellipsis_0.3.2 jquerylib_0.1.4
[57] BiocGenerics_0.40.0 sessioninfo_1.2.2 Rcpp_1.0.9 plyr_1.8.6
[61] prettyunits_1.1.1 ps_1.7.1 viridis_0.6.2 urlchecker_1.0.1
[65] deSolve_1.33 S4Vectors_0.32.4 ggmap_3.0.0 cluster_2.1.2
[69] fs_1.5.2 magrittr_2.0.3 sna_2.6 reprex_2.0.1
[73] mvtnorm_1.1-3 googledrive_2.0.0 tmvnsim_1.0-2 pkgload_1.3.0
[77] xtable_1.8-4 mime_0.12 hms_1.1.2 patchwork_1.1.1
[81] evaluate_0.17 jpeg_0.1-9 IRanges_2.28.0 gridExtra_2.3
[85] compiler_4.1.2 V8_4.2.1 crayon_1.5.2 htmltools_0.5.3
[89] later_1.3.0 ggfun_0.0.4 mgcv_1.8-38 tzdb_0.3.0
[93] aplot_0.1.1 expm_0.999-6 DBI_1.1.1 tweenr_1.0.2
[97] subplex_1.8 dbplyr_2.1.1 MASS_7.3-54 Matrix_1.3-4
[101] ade4_1.7-19 permute_0.9-7 cli_3.4.1 quadprog_1.5-8
[105] parallel_4.1.2 pkgconfig_2.0.3 geosphere_1.5-14 numDeriv_2016.8-1.1
[109] foreign_0.8-81 xml2_1.3.3 memuse_4.2-1 vipor_0.4.5
[113] bslib_0.4.0 rvest_1.0.2 yulab.utils_0.0.4 callr_3.7.2
[117] digest_0.6.30 vegan_2.6-2 rmarkdown_2.17 cellranger_1.1.0
[121] fastmatch_1.1-3 tidytree_0.3.6 datapasta_3.1.0 curl_4.3.3
[125] pairsnp_0.1.0 shiny_1.7.2 geiger_2.0.10 rjson_0.2.20
[129] lifecycle_1.0.3 nlme_3.1-153 jsonlite_1.8.3 seqinr_4.2-16
[133] viridisLite_0.4.1 fansi_1.0.3 pillar_1.8.1 lattice_0.20-45
[137] pkgbuild_1.3.1 fastmap_1.1.0 httr_1.4.4 plotrix_3.8-2
[141] remotes_2.4.2 glue_1.6.2 png_0.1-7 profvis_0.3.7
[145] stringi_1.7.8 sass_0.4.2 ggfittext_0.9.1 textshaping_0.3.6
[149] memoise_2.0.1
Make some shortcuts for plotting
y.theme.strip <- theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y= element_blank())
y.theme.strip.partial <- theme(axis.text.y = element_blank(), axis.ticks.y= element_blank())
x.theme.strip <- theme(axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.partial <- theme(axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.labs <- theme(axis.text.x = element_blank(),axis.title.x = element_blank())
x.theme.axis.rotate <- theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
legend.strip <- theme(legend.position = "none")
theme.text.size <- theme(text = element_text(size = 10))
'%notin%' <- Negate('%in%')
max.font.size <- 7
basic.font.size <- 6
min.font.size <- 5.25
theme.text.size <- theme(text = element_text(size = basic.font.size))
theme.text.size.within <- (5/14)*min.font.size
panel.lab.size <- 10
Specify raw data - global dataset
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Data/"
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/data/"
Data_input_directory <- paste0(getwd(), "/inputdata/")
################################
#### Tree data
# ML tree (refined dataset)
TPA.MLtree.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.fix-zero-dist.treefile")
# Pyjar tree (refined dataset)
TPA.pyjar.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.pyjar.tre")
# Full size BEAST2 analysis - previously generated as part of Beale, 2021.
full.beast2.tree.file <- paste0(Data_input_directory,"TPA-uber_beast2_strict-skyline-500M_10pop_consensus.tree")
# Ancestral reconstruction of global TPA ML tree from TreeTime (refined dataset)
TPA.treetime.ancestral.tree.file <- paste0(Data_input_directory,"TPA.annotated_tree.fix-hung.nexus")
TPA.treetime.ancestral.vcf.file <- paste0(Data_input_directory,"TPA-uber.midpoint.ancestral_sequences.fix-hung.vcf")
# Functionally annotated variants, extracted from snpEff vcf into tsv using snpSift
TPA.snpEff.file <- paste0(Data_input_directory,"TPA-uber.midpoint.ancestral_sequences.relab.bcf.ann.vcf.vartab.sepline.tsv")
# Gff file for SS14 reference genome, containing gene positions/annotations
SS14.gff.file <- paste0(Data_input_directory,"Treponema_pallidum_subs._pallidum_SS14.NC_021508.1.2021-06-13.gff")
################################
#### Meta data
# Supplement from TPA-Uber paper - Beale, 2021
TPA.meta2.file <- paste0(Data_input_directory,"Sup_Data1_Global_Sample-Metadata__09-2022.xlsx")
# England specific metadata collated by PHE/UKHSA
PHE.metadata.linked.file <- paste0(Data_input_directory,"Sup_Data2_TPA.UK-only.PHE.metadata.2022-02-02.xlsx")
# England specific mapping shapefile data with Public Health Boundaries
# Imported datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.publichealth.shapefile.data <- paste0(Data_input_directory,"Public_Health_England_Centres_(December_2016)_Boundaries")
################################
#### Externally plotted figures (e.g. GrapeTree) for inclusion in multipanel figures
# Externally plotted grapetree minimum spanning tree for whole of England - code to extract subtree that was used to make this is included later in this Rnotebook
TPA.UK.Grapetree.sublineages.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.sublineage-MSTree.Inkscaped.svg")
# Externally plotted grapetree minimum spanning tree for whole of England - 3-variable plots
TPA.UK.Grapetree.3way.file <- paste0(Data_input_directory,"TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-3.svg")
# Externally plotted grapetree minimum spanning tree for whole of England - HIV status
TPA.UK.Grapetree.HIV.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.HIVstatus-MSTree_inkscaped.svg")
# Externally plotted grapetree minimum spanning tree for North East England networks
TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")
Specify directory to output plots
Figure_output_directory
[1] "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Github/Syphilis_Genomic_Epi_England_2022-23/Figures_revision_03-2023/"
Read in trees
TPA.MLtree <- midpoint.root(read.tree(TPA.MLtree.file))
TPA.pyjar.tree <- midpoint.root(read.tree(TPA.pyjar.file))
Read in final output metadata from Global Uber study (Beale 2021)
TPA.meta2.1 <- readxl::read_excel(TPA.meta2.file,sheet="Supplementary_Data1_Sample-Meta")
Create a colour scheme for Lineages, Countries and Continents (consistent with Beale, 2021)
# Colouring for country
continental.country.cols.brew2 <- unique(TPA.meta2.1[,c("Geo_Country","Continent")])
continental.country.cols.brew2 <- continental.country.cols.brew2[order(continental.country.cols.brew2$Continent,continental.country.cols.brew2$Geo_Country),]
continental.country.cols.brew2$country.col <- c("#ec7014","#fec44f","#de2d26","#fb6a4a","#bdbdbd","#737373",brewer.pal(n=8,"Purples")[4:8],brewer.pal(n=8,"Blues")[3:8],brewer.pal(n=5,"Greens")[3:5],"#c51b8a","#8c510a")
# Colouring for Continent
continental.cols.brew2 <- data.frame(Continent=sort(unique(TPA.meta2.1$Continent)),stringsAsFactors=F)
continental.cols.brew2$continent.col <- c("#fec44f","#de2d26","#bdbdbd","#2171b5","#74c476","#c51b8a","#ec7014")
# Colouring for TPA Lineage
TPA_Lineage.cols <- data.frame(Lineage=sort(unique(TPA.meta2.1$TPA_Lineage)),stringsAsFactors=F)
TPA_Lineage.cols$Lineage.col <- c("royalblue2", "indianred1")
#c("#436eee", "#666666","#ff6a6a")
TPA_Lineage.cols$Lineage <- factor(TPA_Lineage.cols$Lineage, levels=c("Nichols","SS14","outlier"))
# Lineage Hexcodes
# royalblue2 #436eee
# indianred1 #ff6a6a
Define colours for sublineages
# Define sublineage clustering scheme using brew colourscales
sublineages.cols.brew <- data.frame(unique(TPA.meta2.1[,c("TPA_Lineage","TPA.pinecone.sublineage")]), stringsAsFactors = F)
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$TPA_Lineage,sublineages.cols.brew$TPA.pinecone.sublineage),]
sublineages.cols.brew$sublin.order <- as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))
Warning: NAs introduced by coercion
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$sublin.order),]
# For revised bootstrapped clusters
sublineages.cols.brew$sublineage.cols <- c("#FC9272","#EF3B2C",brewer.pal(n=4,"Greens")[2:4],brewer.pal(n=4,"YlOrBr")[c(2,3)],brewer.pal(n=6,"Blues")[2:6],brewer.pal(n=6,"Purples")[2:6],"grey80","grey80","grey80","grey80")
sublineages.cols.brew <- unique(sublineages.cols.brew[,c("TPA.pinecone.sublineage","sublineage.cols")])
sublineages.cols.brew <- sublineages.cols.brew[order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))),]
Warning in order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))) :
NAs introduced by coercion
sublineages.cols.brew$TPA.pinecone.sublineage <- factor(sublineages.cols.brew$TPA.pinecone.sublineage, levels=sublineages.cols.brew$TPA.pinecone.sublineage)
sublineages.cols.brew <- sublineages.cols.brew[!is.na(sublineages.cols.brew$sublineage),]
colnames(sublineages.cols.brew) <- c("sublineage","sublineage.cols")
sublineages.cols.brew <- unique(sublineages.cols.brew)
Restrict analysis to high quality genomes (and tree)
TPA.meta2.1 <- TPA.meta2.1[TPA.meta2.1$finescale.analysis=="Yes",]
Create a “UK” variable, and a “PHE” variable
TPA.meta2.1$is.UK <- ifelse(TPA.meta2.1$Geo_Country=="UK","UK","Other")
TPA.meta2.1$is.PHE <- ifelse(TPA.meta2.1$Geo_Country=="UK" & grepl("PHE",TPA.meta2.1$Sample_Name),"PHE","Other")
# Prepare ML tree
TPA.MLtree.ggtree <- ggtree(TPA.MLtree,layout = "fan",open.angle = 10, right=T)
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
# Prepare country dataset
TPA.rawseq.countries.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Country=TPA.meta2.1$Geo_Country, stringsAsFactors = F)
# Prepare continent dataset
TPA.rawseq.continents.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Continent=TPA.meta2.1$Continent, stringsAsFactors = F)
# Prepare UK data strip
TPA.rawseq.UK.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, England=TPA.meta2.1$is.UK, stringsAsFactors = F)
TPA.rawseq.UK.p[TPA.rawseq.UK.p$England=="UK",] <- "England"
# Prepare PHE data strip
TPA.rawseq.PHE.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, PHE=TPA.meta2.1$is.PHE, stringsAsFactors = F)
# Prepare Major lineage dataset
TPA.rawseq.Lineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Lineage=TPA.meta2.1$TPA_Lineage, stringsAsFactors = F)
# Prepare sublineage lineage dataset
TPA.rawseq.subLineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F)
# Prepare Year dataset (all samples)
TPA.rawseq.all.Years.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Year=TPA.meta2.1$Sample_Year, stringsAsFactors = F)
floor_5years <- function(value){ return(value - value %% 5) }
TPA.meta2.1$Sample_5year.window <- paste0(floor_5years(as.numeric(TPA.meta2.1$Sample_Year)),"-",floor_5years(as.numeric(TPA.meta2.1$Sample_Year))+5)
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
NAs introduced by coercion
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
NAs introduced by coercion
# Some samples have uncertain dates (up to 20-30 years uncertainty), but for the purposes of these plotting categories we'll use the centrepoint year
TPA.meta2.1$Sample_5year.window <- sapply(1:nrow(TPA.meta2.1), function(x) ifelse(TPA.meta2.1$Sample_Year[x]=="-",NA, ifelse(is.na(TPA.meta2.1$Sample_5year.window[x]),NA, ifelse(TPA.meta2.1$Sample_Year[x]=="1950-1980","1965-1970",ifelse(TPA.meta2.1$Sample_Year[x]=="1960-1980","1965-1970" ,ifelse(TPA.meta2.1$Sample_Year[x]=="1980-1999","1985-1990",TPA.meta2.1$Sample_5year.window[x]))))))
TPA.meta2.1$Sample_year.1990.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1990,TPA.meta2.1$Sample_Year,"<1990")
TPA.meta2.1$Sample_year.1999.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1999,TPA.meta2.1$Sample_Year,"<1999")
TPA.rawseq.year.cuttoff.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sample.Year=TPA.meta2.1$Sample_year.1999.cuttoff, stringsAsFactors = F)
# Bring in PHE metadata
PHE.metadata.linked <- readxl::read_excel(PHE.metadata.linked.file)
Do some cleanup and factoring of variables
PHE.metadata.linked$age_group <- factor(PHE.metadata.linked$age_group, levels=rev(c("16-24","25-34","35-44","45+","Unknown")))
PHE.metadata.linked$london <- factor(PHE.metadata.linked$london,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$ukborn <- factor(PHE.metadata.linked$ukborn,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$hivpos <- factor(PHE.metadata.linked$hivpos, levels=rev(c("Yes","No","Unknown")))
# need to update terminology of 'MSM' to 'GBMSM'
PHE.metadata.linked[PHE.metadata.linked$gender_orientation=="MSM","gender_orientation"] <- "GBMSM"
PHE.metadata.linked$gender_orientation <- factor(PHE.metadata.linked$gender_orientation, levels=rev(c("MSW","GBMSM","WSM","MUnknown","Unknown")))
PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(c("East Midlands", "East of England", "London", "North East", "North West", "South East", "South West", "West Midlands", "Yorkshire and Humber", "UK (not England)", "Not Known")))
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=sublineages.cols.brew$sublineage)
### Extract information about duplicates
PHE.metadata.duplicates <- PHE.metadata.linked[!is.na(PHE.metadata.linked$dup_flag),]
PHE.metadata.duplicates <- PHE.metadata.duplicates[!is.na(PHE.metadata.duplicates$Sample_Name),]
PHE.patient.matches <- data.frame(
stringsAsFactors = FALSE,
dup_flag = c("1A","1B",
"2A","2B","3A","3B","4A",
"4B","5A","5B"),
dup_Patient = c("Patient 1",
"Patient 1","Patient 2",
"Patient 2","Patient 3","Patient 3",
"Patient 4","Patient 4",
"Patient 5","Patient 5"),
dup_Patient_Sample = c("sample 1",
"sample 2","sample 1",
"sample 2","sample 1","sample 2",
"sample 1","sample 2","sample 1",
"sample 2")
)
PHE.metadata.duplicates <- left_join(PHE.metadata.duplicates, PHE.patient.matches, by="dup_flag")
PHE.metadata.duplicates
Duplicate Samples missing metadata are all ‘new duplicates’ and were excluded due to low mapping coverage (all checked).
Samples labelled ‘ZA’ and ‘XB’ had duplicates in the original dataset, but the reciprocal pairs were excluded due to quality isues.
Available pairs - Patient 3, Patient 4
PHE.metadata.duplicates.paired <- PHE.metadata.duplicates[PHE.metadata.duplicates$dup_Patient %in% c("Patient 3","Patient 4"),]
PHE.metadata.duplicates.paired[order(PHE.metadata.duplicates.paired$dup_Patient, PHE.metadata.duplicates.paired$year,PHE.metadata.duplicates.paired$month),c("Sample_Name","dup_Patient", "month.fix", "year")]
These will be revisited later in the analysis.
Patient 4 HIV-ve MSM (45+), UK born, PHE region D 2 samples, collected in the same month and year Both samples are sublineage 1, and identical (0 pwSNPs) Likely the same infection (depending on dates, treatment, etc), but can’t rule out reinfection with same strain.
Patient 3 HIV-ve MSM (35-44), not UK born, based in London (C) 2 samples, collected 9 months apart Both samples are sublineage 1, but have 7 pairwise SNPs between them (loads!) Reinfection – probably from a different transmission network
However, based on the sample dates, as well as the outcome of the downstream genetic analysis, we can see that Patient 3 has duplicate infection events (different dates, 10 months apart) and the genomes are distinct (7 SNPs apart), whereas Patient 4 samples were collected in the same month and year (i.e. are likely duplicates from the same infection) and has identical genomes.
For downstream analysis purposes, we will retain both samples for Patient 3 (discrete infections), but exclude one sample from Patient 4 (duplicate infection samples) - ‘PHE150126A’ has much better genome coverage, so exclude ‘PHE150125A’
### Further Exclusions
PHE130056A - duplicate of PHE130057B (already removed, so not relevant) - don’t exclude! PHE170402A - quality control sample PHE170378A - quality control sample
Exclude duplicate sequences
duplicate.exclusion.list <- c("PHE150125A","PHE170402A","PHE170378A")
PHE.metadata.linked <- PHE.metadata.linked[PHE.metadata.linked$Sample_Name %notin% duplicate.exclusion.list,]
Moving on…
Define some colour schemes
# define some colors for each region
PHE.region.cols.brew <- data.frame(UKHSA.region=c("North East", "North West", "Yorkshire and Humber", "East Midlands", "West Midlands", "East of England", "London", "South East","South West","UK (not England)", "Not Known"), stringsAsFactors=F)
PHE.region.cols.brew$region.col <- c("#A6CEE3","#1F78B4","#CAB2D6","#33A02C","#B2DF8A","#FF7F00","#E31A1C","#FB9A99","#D4BB02","grey75","grey25")
# HIV color scheme
PHE.hiv.cols <- data.frame(hivpos=rev(sort(unique(PHE.metadata.linked$hivpos))), stringsAsFactors=F)
PHE.hiv.cols$hiv.cols <- c("#1f78b4","#b2df8a","grey75")
# Orientation colour scheme
PHE.orientation.cols <- data.frame(orientation=rev(sort(unique(PHE.metadata.linked$gender_orientation))), stringsAsFactors=F)
PHE.orientation.cols$orientation <- factor(PHE.orientation.cols$orientation, levels=rev(sort(unique(PHE.metadata.linked$gender_orientation))), labels=c("MSW","GBMSM","WSM","MUnknown","Unknown"))
PHE.orientation.cols$orientation.cols <- c("#1f78b4","#b2df8a","#fb9a99","#a6cee3","grey75")
# UK born colour scheme
PHE.ukborn.cols <- data.frame(ukborn=rev(sort(unique(PHE.metadata.linked$ukborn))),ukborn.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)
# London based colour scheme
PHE.london.cols <- data.frame(london=rev(sort(unique(PHE.metadata.linked$london))),london.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)
# Age group colour scheme
PHE.Age.cols <- data.frame(age_group=rev(sort(unique(PHE.metadata.linked$age_group))),stringsAsFactors = T)
PHE.Age.cols$age_group.cols <- c(brewer.pal(n=4,"YlGnBu"),"grey75")
# Sample Date colour scheme
PHE.year.cols <- data.frame(year=(sort(unique(PHE.metadata.linked$year))),stringsAsFactors = T)
PHE.year.cols$year.cols <- brewer.pal(n=7,"YlOrRd")
# Sample Date (all global data, but with 1990 cuttoff)
TPA.year.cuttoff.cols <- data.frame(date.cuttoff=c("<1999",1999:2019), date.cuttoff.col=c("#F2F2F2",colorRampPalette(brewer.pal(7, "YlOrRd"))(length(1999:2019))))
##### ## First describe the sequenced population as a whole
Set order of PHE regions
PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(PHE.region.cols.brew$UKHSA.region))
Generate some basic statistics about geographical PHE regions (anonymised)
PHE.count.all <- PHE.metadata.linked %>%
dplyr::summarise(count.per.region=n())
PHE.count.years <- PHE.metadata.linked %>%
dplyr::group_by(year) %>%
dplyr::summarise(count.per.year=n()) %>%
ungroup() %>%
dplyr::mutate(perc.per.year=(count.per.year/sum(count.per.year))*100)
# Generate some stats about HIV status
PHE.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(hivpos) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.region) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(HIV.perc=(Count/sum(Count)*100))
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(gender_orientation) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(orientation.perc=(Count/sum(Count)*100))
# Generate some stats about UK born (vague category that's unfortunately only marginally helpful)
PHE.UKborn.counts <- PHE.metadata.linked %>%
dplyr::group_by(ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(UKborn.perc=(Count/sum(Count)*100))
# Generate some stats about London based
PHE.London.counts <- PHE.metadata.linked %>%
dplyr::group_by(london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(London.perc=(Count/sum(Count)*100))
# Generate some stats about Age group
PHE.Age.counts <- PHE.metadata.linked %>%
dplyr::group_by(age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Age.perc=(Count/sum(Count)*100))
# Generate some stats about Lineage group
PHE.Lineage.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))
# Generate some stats about sublineage group
PHE.sublineage.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Sublineage.perc=(Count/sum(Count)*100))
Make some plots
# Make hbar plot of sample counts by region
p.all.hbarplot <- ggplot(PHE.count.all, aes(x=count.per.region,y="")) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(values="grey30") +
geom_text(data=PHE.count.all, aes((count.per.region+12), "",label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
labs(y="All", x="Sample Count") +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(nrow=4))
#p.all.hbarplot
# make temporal bubbleplot of counts by region
p.all.year.bubbleplot <- ggplot(PHE.count.years, aes(as.numeric(year), y="All")) +
geom_point(alpha=0.65, aes(size=count.per.year)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2)) +
theme_light() +
scale_fill_manual(values="grey30") +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
labs(y="", x="Sample Year", size="Count")
#p.all.year.bubbleplot
# Make proportional hbar plot of HIV status
p.all.hiv.hbarplot <- ggplot(PHE.HIV.counts, aes(Count,y="",fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="All", x="HIV +ve") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.HIV.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
#p.all.hiv.hbarplot
p.all.orientation.hbarplot <- ggplot(PHE.orientation.counts, aes(Count,y="",fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="All", x="Orientation") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.orientation.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.orientation.hbarplot
p.all.ukborn.hbarplot <- ggplot(PHE.UKborn.counts, aes(Count,y="",fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="UK\nBorn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="All", x="UK Born") +
#guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.UKborn.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.ukborn.hbarplot
p.all.London.hbarplot <- ggplot(PHE.London.counts, aes(Count,y="",fill=london)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
labs(y="All", x="London") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.London.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.London.hbarplot
p.all.Age.hbarplot <- ggplot(PHE.Age.counts, aes(Count,y="",fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="All", x="Age Group") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.Age.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.Age.hbarplot
Plot combined plot for ‘all samples’
PHE.all.combiplot.1 <- plot_grid(p.all.year.bubbleplot, p.all.hbarplot + y.theme.strip, p.all.orientation.hbarplot + y.theme.strip, p.all.hiv.hbarplot + y.theme.strip, p.all.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.all.combiplot.1

Next just describe population distributions by PHE region
# generate some basic statistics about geographical PHE regions (anonymised)
PHE.geo.count <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre) %>%
dplyr::summarise(count.per.region=n()) %>%
dplyr::mutate(total.count=sum(count.per.region),fraction=count.per.region/total.count)
PHE.geo.count.years <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,year) %>%
dplyr::summarise(count.per.region.year=n())
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,year,TPA_Lineage) %>%
dplyr::summarise(count.per.region.year=n()) %>%
dplyr::mutate(total.count.year=sum(count.per.region.year)) %>%
dplyr::ungroup() %>%
tidyr::pivot_wider(names_from=TPA_Lineage, values_from = count.per.region.year)
`summarise()` has grouped output by 'phe_centre', 'year'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage[is.na(PHE.geo.count.years.lineage)] <- 0
PHE.geo.count.years.lineage$year <- as.numeric(PHE.geo.count.years.lineage$year)
# Generate some stats about HIV status
PHE.geo.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,hivpos) %>%
dplyr::summarise(count.per.region.hiv=n()) %>%
dplyr::mutate(total.region=sum(count.per.region.hiv)) %>%
dplyr::mutate(fraction=count.per.region.hiv/total.region) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Double Check HIV status data for non-PHE dataset - confirmed no HIV+ves from non-MSM.
PHE.sourcelab.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(is.PHE, gender_orientation, hivpos) %>%
dplyr::summarise(count.per.orientation.hiv=n()) #%>%
`summarise()` has grouped output by 'is.PHE', 'gender_orientation'. You can override using the `.groups` argument.
#dplyr::filter(is.PHE!="PHE")
# Get total population stats for HIV
PHE.all.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(hivpos) %>%
dplyr::summarise(count.hiv=n()) %>%
dplyr::mutate(count.total=sum(count.hiv), fraction=count.hiv/count.total)
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(gender_orientation) %>%
dplyr::summarise(orientation.count=n()) %>%
dplyr::mutate(orientation.percent=(orientation.count/sum(orientation.count)*100))
PHE.geo.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,gender_orientation) %>%
dplyr::summarise(count.per.region.orientation=n()) %>%
dplyr::mutate(total.region=sum(count.per.region.orientation)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=count.per.region.orientation/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(orientation.percent=(count.per.region.orientation/sum(count.per.region.orientation)*100))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.geo.UKborn <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about London based
PHE.geo.London <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.geo.Age <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Lineage group
PHE.geo.Lineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about sublineage group
PHE.geo.sublineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
Make some plots
# Make hbar plot of sample counts by region
p.region.hbarplot <- ggplot(PHE.geo.count, aes(count.per.region,phe_centre, fill=phe_centre)) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
geom_text(data=PHE.geo.count, aes((count.per.region+12), phe_centre,label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
labs(y="UKHSA Region", x="Sample Count") +
#coord_cartesian(xlim=c(0,130)) +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(ncol=2))
#p.region.hbarplot
# make temporal bubbleplot of counts by region
p.region.year.bubbleplot <- ggplot(PHE.geo.count.years, aes(as.numeric(year), phe_centre, colour=phe_centre)) +
geom_point(alpha=0.65, aes(size=count.per.region.year)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
theme_light() +
scale_color_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
labs(y="UKHSA Region", x="Sample Year", size="Count")
#p.region.year.bubbleplot
# Or a barplot of lineage by year & PHE region?
p.region.year.bubbleplot.barplot.facet.lineage <- PHE.geo.count.years.lineage %>% tidyr::pivot_longer(c(SS14, Nichols), names_to="TPA_Lineage", values_to="Count") %>%
ggplot(aes(year, Count, fill=TPA_Lineage)) +
geom_bar(stat='identity', width=0.6) +
facet_grid(phe_centre~., scales='free') +
guides(size=guide_legend(nrow=2)) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y = element_text(color = "grey25", size=7, angle=0))
#p.region.year.bubbleplot.barplot.facet.lineage
# Make proportional hbar plot of HIV status
p.region.hiv.hbarplot <- ggplot(PHE.geo.HIV.counts, aes(count.per.region.hiv,phe_centre,fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="UKHSA Region", x="HIV +ve") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.HIV.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.hiv), size=theme.text.size.within, inherit.aes = F) +
NULL
#p.region.hiv.hbarplot
p.region.orientation.hbarplot <- ggplot(PHE.geo.orientation.counts, aes(count.per.region.orientation,phe_centre,fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="UKHSA Region", x="Orientation") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.geo.orientation.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.orientation), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot
p.region.ukborn.hbarplot <- ggplot(PHE.geo.UKborn, aes(Count,phe_centre,fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UK Born",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="UKHSA Region", x="UK Born") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.UKborn, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.ukborn.hbarplot
p.region.London.hbarplot <- ggplot(PHE.geo.London, aes(Count,phe_centre,fill=london)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
labs(y="UKHSA Region", x="London") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.London, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.London.hbarplot
p.region.Age.hbarplot <- ggplot(PHE.geo.Age, aes(Count,phe_centre,fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="UKHSA Region", x="Age Group") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.geo.Age, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.Age.hbarplot
Combined plot
PHE.region.combiplot.1 <- plot_grid(p.region.year.bubbleplot, p.region.hbarplot + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip, p.region.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.region.combiplot.1

Regions as a complex multipanel plot
# legends
PHE.region.combiplot.1.legends <- plot_grid(get_legend(p.region.year.bubbleplot), get_legend(p.region.hbarplot + y.theme.strip), get_legend(p.region.orientation.hbarplot + y.theme.strip), get_legend(p.region.hiv.hbarplot + y.theme.strip), get_legend(p.region.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)
# Arrange plots vertically
p.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.region.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.region.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.region.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
# Combine the plots
p.region.hbar.combi.plus.all <- plot_grid(p.year.bubbleplot.combi, p.region.hbar.counts.combi, p.region.hbar.orientation.combi, p.region.hbar.hiv.combi, p.region.hbar.Age.combi, nrow=1, rel_widths=c(6,4,4,4,4), labels = c("A","B","C","D","E"), label_size=panel.lab.size, vjust=0.25)
# and add the legends on top
p.region.hbar.combi.plus.all.with.legends <- plot_grid(p.region.hbar.combi.plus.all, PHE.region.combiplot.1.legends, ncol=1, rel_heights=c(6,1), scale = 0.95)
p.region.hbar.combi.plus.all.with.legends

#ggsave(paste0(Figure_output_directory, "SupFig2_TPA-PHE_Sample-metadistros-by-phe_region+all-combi.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=240, height=135, device='pdf', dpi=1200)
Now lets look at some genetic data
### Make ML tree with sublineage tippoints
TPA.MLtree.ggtree.tippoint <- TPA.MLtree.ggtree %<+% data.frame(Sample_Name=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F) +
geom_tippoint(aes(color=Sublineage), size=0.5, alpha=0.5, show.legend = FALSE) +
scale_color_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
Add metadata
# Continent
p.TPA.MLtree.PHE <- gheatmap(TPA.MLtree.ggtree.tippoint,
TPA.rawseq.continents.p, color=NULL,width=0.075,offset=0.00000025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Continent",values=continental.cols.brew2$continent.col, breaks=continental.cols.brew2$Continent, guide = guide_legend(order = 1,ncol=2)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# is UK
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,
TPA.rawseq.UK.p, color=NULL,width=0.075,offset=0.00001025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="England/Other", values=c("black","grey95"), breaks=c("England","Other"), guide = guide_legend(order = 2,ncol=2)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Lineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,TPA.rawseq.Lineage.p, color=NULL,width=0.075,offset=0.00002025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Lineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage, guide = guide_legend(order = 3, ncol=2)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# sublineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F), color=NULL,width=0.075,offset=0.00003025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 4, ncol=3)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
plot
p.TPA.MLtree.PHE

#ggsave(paste0(Figure_output_directory, "SupFig3_TPA-PHE_Global_Phylo+UK-highlights.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=160, device='pdf', dpi=1200)
### Geographic distributions of Lineages and Sublineages What about sublineages?
p.region.Lineage.hbarplot <- ggplot(PHE.geo.Lineage, aes(Count,phe_centre,fill=TPA_Lineage)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
labs(y="UKHSA Region", x="TPA Lineage") +
guides(fill=guide_legend(nrow=3)) +
#geom_text(data=PHE.geo.Lineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
p.region.sublineage.hbarplot <- ggplot(PHE.geo.sublineage, aes(Count,phe_centre,fill=TPA.pinecone.sublineage)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="UKHSA Region", x="TPA Sublineage") +
guides(fill=guide_legend(nrow=4)) +
#geom_text(data=PHE.geo.sublineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
Combi plot (geography lineages)
PHE.region.combiplot.2.lineages <- plot_grid(p.region.year.bubbleplot +legend.strip, p.region.hbarplot + y.theme.strip + legend.strip + coord_cartesian(xlim=c(0,150)), p.region.Lineage.hbarplot + y.theme.strip +legend.strip, p.region.sublineage.hbarplot + y.theme.strip +legend.strip, nrow=1, align="h", rel_widths=c(6,3,4,4), scale=0.99, labels=c("C","D","E","F"), label_size=panel.lab.size)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# separate out the plot for the legends
p.region.year.bubbleplot.legend <- get_legend(p.region.year.bubbleplot)
p.region.hbarplot.legend <- get_legend(p.region.hbarplot + y.theme.strip)
p.region.Lineage.hbarplot.legend <- get_legend(p.region.Lineage.hbarplot + y.theme.strip)
p.region.sublineage.hbarplot.legend <- get_legend(p.region.sublineage.hbarplot + y.theme.strip)
PHE.region.combiplot.2.lineages.legend <- plot_grid(p.region.year.bubbleplot.legend, p.region.hbarplot.legend, p.region.Lineage.hbarplot.legend, p.region.sublineage.hbarplot.legend, nrow=1, align="h", rel_widths=c(6,3,4,4))
PHE.region.combiplot.2.lineages <- plot_grid(PHE.region.combiplot.2.lineages, PHE.region.combiplot.2.lineages.legend, rel_heights = c(4,1), ncol=1)
PHE.region.combiplot.2.lineages

OK, let’s now add a map of these geographical distributions
Let’s used ONS published shape files - there is one available that shows Public Health England region boundaries.
# Generate approximate regional GPS coords
PHE.region.GPS <- data.frame(
stringsAsFactors = FALSE,
phe_centre = c("East Midlands",
"East of England","London","North East","North West",
"South East","South West","West Midlands",
"Yorkshire and Humber","UK (not England)","Not Known"),
Longitude = c(-0.7,0.5,-0.2,-1.9,-2.4,
0.05,-2.9,-2,-0.8,0.1,0.63),
Latitude = c(52.9,52.4,51.5,55,53.7,
51.1,51,52.6,53.8,54.7,54.1)
)
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[4] <- "SS14"
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="Nichols",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[5] <- "Nichols"
PHE.region.GPS[is.na(PHE.region.GPS)] <- 0
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","total.region")], by="phe_centre")
colnames(PHE.region.GPS)[6] <- "Region_Count"
PHE.region.GPS$radius <- 0.5*(1-1/sqrt(PHE.region.GPS$Region_Count))
###############################
# Import datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.shapefile <- readOGR(dsn=UK.publichealth.shapefile.data)
Warning in OGRSpatialRef(dsn, layer, morphFromESRI = morphFromESRI, dumpSRS = dumpSRS, :
Discarded datum Ordnance_Survey_of_Great_Britain_1936 in Proj4 definition: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs
OGR data source with driver: ESRI Shapefile
Source: "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Github/Syphilis_Genomic_Epi_England_2022-23/inputdata/Public_Health_England_Centres_(December_2016)_Boundaries", layer: "Public_Health_England_Centres_(December_2016)_Boundaries"
with 9 features
It has 9 fields
#Reshape for ggplot2 using the Broom package
UK.mapdata <- tidy(UK.shapefile, region="phec16nm")
#UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color = "#FFFFFF", size = 0.25)
UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075)
#UK.gg <- UK.gg + coord_fixed(1) + theme_nothing()
#UK.gg
# Map plotting file becomes _very_ big - use ggrastr to reduce the size
UK.gg <-ggplot() + ggrastr::rasterise(geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075), dpi=400) + coord_fixed(1) + theme_nothing()
#rasterise(geom_point(aes(carat, price, colour = cut), data=diamonds), dpi=30)
# Convert UK regions to be compatible with map
# First find centre point for each region
UK.mapdata.regions.meancoords <- UK.mapdata %>% dplyr::group_by(id) %>%
dplyr::summarise(mean.lat=mean(lat), mean.long=median(long)) %>%
dplyr::ungroup()
colnames(UK.mapdata.regions.meancoords)[1] <- "phe_centre"
PHE.region.GPS.ukmap <- dplyr::left_join(PHE.region.GPS, UK.mapdata.regions.meancoords, by="phe_centre")
# Add artificial location for 'not known'
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.lat"] <- 600000
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.long"] <- 550000
# Shift "South East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="South East","mean.long"] <- 475000
# Shift "East of England East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="East of England","mean.lat"] <- 275000
# Not going to try plotting the 2 samples from elsewhere in the UK, so remove that row
PHE.region.GPS.ukmap <- PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre != "UK (not England)",]
# Create radius variable for plotting pie sizes (use log10(n)*20,000)
PHE.region.GPS.ukmap$radius.UK <- log10(PHE.region.GPS.ukmap$Region_Count)*20000
#PHE.geo.count.years.lineage
UK.gg.scatterpie <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap, aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("Nichols","SS14")) +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) + theme(legend.position="top")
UK.gg.scatterpie <- UK.gg.scatterpie + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)
UK.gg.scatterpie <- UK.gg.scatterpie + theme_nothing()
#? Add labels
UK.gg.scatterpie.labs <- UK.gg.scatterpie + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size = 0.1) + theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") +
theme.text.size +
theme_nothing()
UK.gg.scatterpie.labs

Now do an equivalent plot for sublineages
PHE.region.GPS.ukmap.sublin <- PHE.region.GPS.ukmap
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="1",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[11] <- "1"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="2",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[12] <- "2"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="3",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[13] <- "3"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="6",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[14] <- "6"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="8",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[15] <- "8"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[16] <- "14"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="15",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[17] <- "15"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="16",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[18] <- "16"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="Singleton",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[19] <- "Singleton"
PHE.region.GPS.ukmap.sublin[is.na(PHE.region.GPS.ukmap.sublin)] <- 0
# Most samples are either sublineage 1 or 14. Let's create a count of samples that are neither.
PHE.region.GPS.ukmap.sublin$`Other Sublineages` <- sapply(1:nrow(PHE.region.GPS.ukmap.sublin), function (x) PHE.region.GPS.ukmap.sublin$Region_Count[x]-sum(PHE.region.GPS.ukmap.sublin$`1`[x], PHE.region.GPS.ukmap.sublin$`14`[x]))
UK.gg.scatterpie.sublineage <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap.sublin[PHE.region.GPS.ukmap.sublin$mean.long!=0,], aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("1","14","Other Sublineages")) +
scale_fill_manual(name="TPA\nSublineage",values=c("#FC9272","#BCBDDC", "grey50"), breaks=c("1","14","Other Sublineages"))
# add legend
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)
#UK.gg.scatterpie <- UK.gg.scatterpie + x.theme.strip + y.theme.strip
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + theme_nothing()
#? Add labels
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size = 0.1) +
theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") +
theme.text.size +
theme_nothing()
UK.gg.scatterpie.sublineage

Combined map plot
UK.gg.scatterpie.combi <- plot_grid(UK.gg.scatterpie.labs, UK.gg.scatterpie.sublineage, ncol=2, labels = c("A","B"), label_size=panel.lab.size)
UK.gg.scatterpie.combi

Plot in combination with barplots
plot_grid(UK.gg.scatterpie.combi, PHE.region.combiplot.2.lineages, nrow=2, rel_heights=c(4,5))

#ggsave(paste0(Figure_output_directory,"Fig2_TPA-PHE_Map-Lineage+Barplots.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)
### Analysis by sublineage
Now lets start exploring how samples are distributed by sublineage
PHE.metadata.linked <- PHE.metadata.linked
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=rev(as.character(sort(unique(PHE.metadata.linked$TPA.pinecone.sublineage)))))
PHE.Lineage.count <- PHE.metadata.linked %>%
dplyr::group_by(TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total=sum(Count), perc=(Count/total)*100)
PHE.sublin.count <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total=sum(Count), perc=(Count/total)*100)
PHE.geo.sublin.years <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,year) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
## Generate some stats about sublineage groups
# Generate some stats about gender orientation
PHE.sublineage.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,gender_orientation) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.sublineage.UKborn <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
#dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about London based
PHE.sublineage.London <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.sublineage.Age <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about HIV group
PHE.sublineage.HIV <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, hivpos) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats by PHE Region
PHE.sublineage.PHEcentre <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, phe_centre) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(phe_centre), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
Plot by sublineage
p.sublineage.year.bubbleplot <- ggplot(PHE.geo.sublin.years, aes(as.numeric(year), TPA.pinecone.sublineage, colour=TPA.pinecone.sublineage)) +
geom_point(alpha=0.65, aes(size=Count)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
theme_light() +
scale_color_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
labs(y="TPA Sublineage", x="Sample Year", size="Count")
#p.sublineage.year.bubbleplot
p.sublineage.hbarplot <- ggplot(PHE.sublin.count, aes(Count,TPA.pinecone.sublineage,fill=TPA.pinecone.sublineage)) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="TPA Sublineage", x="Sample Count") +
geom_text(data=PHE.sublin.count, aes((Count+12), TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F) +
#coord_cartesian(xlim=c(0,200)) +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(ncol=2))
#p.sublineage.hbarplot
p.sublineage.orientation.hbarplot <- ggplot(PHE.sublineage.orientation.counts, aes(y=TPA.pinecone.sublineage,x=Count,fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="TPA Sublineage", x="Orientation") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.orientation.counts, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot
p.sublineage.hiv.hbarplot <- ggplot(PHE.sublineage.HIV, aes(y=TPA.pinecone.sublineage, x=Count,fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="TPA Sublineage", x="HIV +ve") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.HIV, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.hiv.hbarplot
p.sublineage.ukborn.hbarplot <- ggplot(PHE.sublineage.UKborn, aes(y=TPA.pinecone.sublineage,x=Count,fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UK\nborn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="TPA Sublineage", x="UK born") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.sublineage.UKborn, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.ukborn.hbarplot
p.sublineage.Age.hbarplot <- ggplot(PHE.sublineage.Age, aes(y=TPA.pinecone.sublineage, x=Count ,fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="TPA Sublineage", x="Age Group") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.Age, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.Age.hbarplot
p.sublineage.PHEregion.hbarplot <- ggplot(PHE.sublineage.PHEcentre, aes(y=TPA.pinecone.sublineage, x=Count, fill=phe_centre)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$PHE.region) +
labs(y="TPA Sublineage", x="UKHSA Region") +
guides(fill=guide_legend(nrow=4)) +
geom_text(data=PHE.sublineage.PHEcentre, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
Look at how sublineages are distributed by region (sublineage-centric)
p.sublineage.PHEregion.hbarplot

Combine patient metadata into a plot
#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, p.sublineage.ukborn.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,2,2), scale=0.9)
#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,4), scale=0.9)
PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.sublineages.combiplot.1

Lets add the ‘all’ row again to the ‘by sublineage’ plot
# legends
PHE.sublineage.combiplot.1.legends <- plot_grid(get_legend(p.sublineage.year.bubbleplot), get_legend(p.sublineage.hbarplot + y.theme.strip), get_legend(p.sublineage.orientation.hbarplot + y.theme.strip), get_legend(p.sublineage.hiv.hbarplot + y.theme.strip), get_legend(p.sublineage.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)
# regions
#PHE.sublineage.combiplot.1.nolegend <- plot_grid(p.sublineage.year.bubbleplot + legend.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
# Or do it vertically
p.sublineage.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.sublineage.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
# Combine the plots
p.sublineage.hbar.combi.plus.all <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,3,4,4,4), labels=c("A", "B", "C", "D", "E"),label_size=panel.lab.size, vjust=1, scale=0.99)
# and add the legends on top
#p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(PHE.sublineage.combiplot.1.legends, p.sublineage.hbar.combi.plus.all, ncol=1, rel_heights=c(1,9))
# legends below
p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(p.sublineage.hbar.combi.plus.all, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(8,1))
p.sublineage.hbar.combi.plus.all.with.legends

These patterns look fairly similar between sublineages, and (apart from 1 & 14) the groups are very small. However, sublineage 14 does appear to have a higher proportion of MSM compared to sublineage 1 and others. Let’s test that formally using 2x2 fisher’s tests
PHE.MSM.counts.all <- PHE.metadata.linked %>%
dplyr::group_by(is.MSM, .drop=F) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange((is.MSM), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
PHE.sublineage.MSM.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,is.MSM, .drop=F) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange((is.MSM), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) #%>%
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
#dplyr::filter(!is.na(is.MSM))
PHE.sublineage.MSM.counts.wider <- PHE.sublineage.MSM.counts %>% dplyr::select(TPA.pinecone.sublineage, is.MSM, Count) %>%
tidyr::pivot_wider(names_from = is.MSM, values_from=Count) %>%
dplyr::mutate(MSM=replace_na(MSM, 0), Other=replace_na(Other, 0), Total=sum(MSM,Other)) %>%
#dplyr::select(-`NA`) %>%
dplyr::filter(Total!=0)
PHE.sublineage.MSM.pval <- data.frame(TPA.pinecone.sublineage=PHE.sublineage.MSM.counts.wider$TPA.pinecone.sublineage, p.fisher=sapply(1:nrow(PHE.sublineage.MSM.counts.wider), function (x) fisher.test(matrix(as.numeric(c(PHE.sublineage.MSM.counts.wider[x,"MSM"],
PHE.sublineage.MSM.counts.wider[x,"Other"],
PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="MSM","Count"], PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="Other","Count"])),nrow=2))[[1]]), stringsAsFactors=F)
PHE.sublineage.MSM.counts.wider <- dplyr::left_join(PHE.sublineage.MSM.counts.wider, PHE.sublineage.MSM.pval, by="TPA.pinecone.sublineage")
PHE.sublineage.MSM.counts.wider
### Visualisation of UK genomic relationships
Ok, let’s make a tree for displaying these relationships using the UK dataset only
From some experimentation, a ‘GrapeTree’ minimum spanning network works well for visualising the clonality of these populations. We can use a SNP-scaled phylogeny as direct input to GrapeTree, and this will allow branches to be scaled appropriately. However, although annotation is allowed within the GrapeTree software, colours must be manually edited. Final GrapeTree plots can then be imported back into R for combining with other plots.
Alternative visualisations - grapetree?
Take the 526-global phylogeny (snp-scaled version from pyjar), and prune to only include the UK strains from this study - this ensures the topology is consistent accross studies.
TPA.pyjar.tree.subset.uk <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Sample_Name"])))
TPA.pyjar.tree.subset.global_beast_only.seqlanes <- TPA.meta2.1 %>% filter(full.temporal.analysis=='Yes') %>%
select(Cleaned_fastq_id) %>% pull()
TPA.pyjar.tree.subset.uk.seqlanes <- as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Cleaned_fastq_id"]))
ggtree(TPA.pyjar.tree.subset.uk)

#write.tree(TPA.pyjar.tree.subset.uk, paste0(Data_input_directory,"TPA.UK-only.pyjar.2022-02-03.tre"))
# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"
#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)
Tree independently visualised and annotated using GrapeTree.
Now import and integrate GrapeTree plot with metadata plots.
# Combine the plots
p.sublineage.hbar.combi.plus.all.B2F <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,4,4,4,4), labels=c("B", "C", "D", "E", "F"),label_size=panel.lab.size, vjust=1, scale=0.97)
# legends below
p.sublineage.hbar.combi.plus.all.with.legends.B2F <- plot_grid(p.sublineage.hbar.combi.plus.all.B2F, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(7,1))
#p.sublineage.hbar.combi.plus.all.with.legends.B2F
# Now bring in externally plotted Grapetree
p.TPA.UK.Grapetree.sublineages <- ggdraw() + draw_image(TPA.UK.Grapetree.sublineages.file)
p.TPA.UK.Grapetree.sublineages

p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree <- plot_grid(p.TPA.UK.Grapetree.sublineages, p.sublineage.hbar.combi.plus.all.with.legends.B2F, ncol=1, labels=c("A",""), label_size=panel.lab.size, rel_heights=c(3,5))
p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree

#ggsave(paste0(Figure_output_directory, "Fig1_TPA-PHE_Sample-distros-sublineage.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)
Manage other GrapeTree plots (for consistency)
TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-2
# Bring in 3-way graphetree plot (3 different metadata variables using the same input tree)
TPA.UK.Grapetree.3way <- ggdraw() + draw_image(TPA.UK.Grapetree.3way.file)
TPA.UK.Grapetree.3way

#ggsave(paste0(Figure_output_directory, "SupFig4_TPA-PHE_Grapetree-3ways.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=145, height=180, device='pdf', dpi=1200)
And also do the HIV status plot
TPA.UK.Grapetree.HIV <- ggdraw() + draw_image(TPA.UK.Grapetree.HIV.file)
TPA.UK.Grapetree.HIV

#ggsave(paste0(Figure_output_directory, "SupFig5_TPA-PHE_Grapetree-HIV.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=110, device='pdf', dpi=1200)
### Phylogenetic context analyses
Ok, now lets look at some trees
First, let’s formalise BEAST tree plotting as three separate functions to enable other trees to be plotted the same way
full.beast2.tree <- read.beast(full.beast2.tree.file)
full.beast2.tree@phylo$tip.label <- gsub("\\|.+$","",full.beast2.tree@phylo$tip.label, perl=T)
################################################################################################
# function to extract a tree based on sublineage
Extract_sublineage_tree_for_plot <- function(my.beast.tree, my.metadata, my.phe.meta, my.sublineage){
# get all tips to include from metadata, then calculate MRCA from tree
sublineage.test.mrca <- getMRCA(my.beast.tree@phylo, as.character(unlist(my.metadata[my.metadata$TPA.pinecone.sublineage==my.sublineage,"Sample_Name"])))
######
TPA.beast.subtree.test <- tree_subset(my.beast.tree, node=sublineage.test.mrca, levels_back=0)
return(TPA.beast.subtree.test)
}
#Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1)
################################################################################################
################################################################################################
# Function to prepare a beast tree with timescale indicators, posterior support and 95% HPD bars
plot_beast_subtree_with_HPD <- function(my.beast.tree, my.metadata, my.phe.meta, mrsd.fulltree){
# get MRCD for tree
mrsd.Beast.tree.test.s <- max(as.numeric(unlist(my.metadata[my.metadata$Sample_Name %in% my.beast.tree@phylo$tip.label,"Sample_Year"])))
mrsd.Beast.tree.test <- lubridate::ymd(paste0(mrsd.Beast.tree.test.s,"-06-01"))
mrsd.Beast.tree.fulltree <- lubridate::ymd(mrsd.fulltree)
#mrsd.Beast.tree.test
# plot basic tree
options(ignore.negative.edge=TRUE)
p.TPA.beast.subtree.test <- ggtree(my.beast.tree, mrsd=mrsd.Beast.tree.test, ladderize = T, size=0.4) + scale_x_continuous(breaks=seq(1960,2020,10), minor_breaks=seq(2000, 2020, 1)) +
theme_tree2() +
# Add date lines for easy interpretation
theme(panel.grid.major = element_line(color="grey50", size=.2),
panel.grid.minor = element_line(color="grey85", size=.2),
panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank())
# Add posterior support as node points
p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.8)),color="gray60",size=2,alpha=0.5, shape=18) +
geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.91)),color="gray40",size=3,shape=18,alpha=0.5) +
geom_point2(aes(subset=(!isTip & as.numeric(posterior)>=0.96)),color="black",size=3,shape=18,alpha=0.5)
######
# extract 95% HPD intervals - geom_range seems unable to do correctly with this tree (known bug for tip dated trees), so extract data and plot using geom_segment
TPA.beast.subtree.test.data <- fortify(my.beast.tree)
minmax <- t(matrix(unlist(TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"height_0.95_HPD"]),nrow=2))
bar_df <- data.frame(node_id=TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"node"],as.data.frame(minmax))
names(bar_df) <- c('node_id','min','max')
bar_df <- bar_df %>% filter(node_id > Ntip(my.beast.tree@phylo))
bar_df <- bar_df %>% left_join(TPA.beast.subtree.test.data, by=c('node_id'='node')) #%>% select(node_id,min,max,y)
#mrcd.decimal <- decimal_date(mrsd.Beast.tree.test)
mrcd.decimal <- decimal_date(mrsd.Beast.tree.fulltree)
# Now add HPDs to plot
p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_segment(aes(x=mrcd.decimal-max, y=y, xend=mrcd.decimal-min, yend=y), data=bar_df, color='red', alpha=0.2, size=2.0)
# Output tree
return(p.TPA.beast.subtree.test)
}
################################################################################################
################################################################################################
# Function to add metadata to tree
# Has two optional arguments "initial.track.offset" and "track.scaling" which can be used to alter the width and positioning of metadata tracks
plot_beast_subtree_with_PHE_metadata <- function(my.beast.tree.input, my.metadata, my.phe.meta, initial.track.offset, track.scaling){
# Add code to allow scaling up of the track offsets and widths - useful for much bigger length trees
if(missing(initial.track.offset)){
initial.track.offset <- 0
}
if(missing(track.scaling)){
track.scaling <- 1
}
# Calculate amount to offset each heatmap track
offset.dist <- 4*track.scaling
track.width <- (1/max(my.beast.tree.input$data$height)*3)*track.scaling
# make a list of taxa used in this plot
my.taxa.list <- as.character(unlist(filter(my.beast.tree.input$data, isTip==TRUE) %>% select(label)))
# make a color scale for sampling years
#PHE.sublintest.year.cols <- data.frame(year=sort(unique(as.numeric(unlist(my.metadata[(my.metadata$Sample_Name %in% my.taxa.list),"Sample_Year"],use.names=F)))),stringsAsFactors = T)
#PHE.sublintest.year.cols$year.cols <- colorRampPalette(brewer.pal(7, "YlOrRd"))(nrow(PHE.sublintest.year.cols))
# Or alternatively, use a common colour scheme for all data (maybe more sensible)
PHE.sublintest.year.cols <- data.frame(year=TPA.year.cuttoff.cols$date.cuttoff, year.cols=TPA.year.cuttoff.cols$date.cuttoff.col, stringsAsFactors = F)
# make metadata file for UK regions present in sublineage
sublin.test.region.meta <- data.frame(row.names=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"Sample_Name"])), Region=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"phe_centre"])), stringsAsFactors = F)
# Add heatmap strips
# Sample Year
#TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.all.Years.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
#scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
#ggnewscale::new_scale_fill()
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.year.cuttoff.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
ggnewscale::new_scale_fill()
# Add country
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, TPA.rawseq.countries.p, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*2),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="Country", values=continental.country.cols.brew2$country.col, breaks=continental.country.cols.brew2$Geo_Country, guide = guide_legend(order = 2)) +
ggnewscale::new_scale_fill()
# UK or non-UK
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional,
TPA.rawseq.UK.p, color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*3), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="England/Other", breaks=c("England","Other"), values=c("black","grey95"), na.value = "white", guide = guide_legend(order = 3, ncol=2)) +
ggnewscale::new_scale_fill()
# UK PHE region
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, sublin.test.region.meta, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*4),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="UKHSA Region", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, na.value = "white", guide = guide_legend(order = 4)) +
ggnewscale::new_scale_fill()
# TPA sublineage
#TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F), color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*5), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=2.5) +
#scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 5))
TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
geom_rootedge(2) +
NULL
# calculate number of taxa
test.taxacount <- length(my.taxa.list)
# Adjust final plot x and y axis to make space for labels using taxa counts
x.axis.limits <- ggplot_build(TPA.beast.subtree.test.global.plot1.regional)$layout$panel_scales_x[[1]]$range$range
TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional +
coord_cartesian(y=c(-0.5-(test.taxacount/15),test.taxacount+2), x=c(x.axis.limits[1],x.axis.limits[2]+3))
return(TPA.beast.subtree.test.global.plot1.regional)
}
################################################################################################
Great, now let’s plot a full beast tree
# function for x-axis time breaks needs tweaking for the full tree
TPA.Global.full.BeastTree.ukmeta <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(my.beast.tree = full.beast2.tree, my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, mrsd.fulltree = "2019-06-01") + scale_x_continuous(breaks=seq(1400,2020,50), minor_breaks=seq(1950, 2020, 5)), my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, track.scaling = 5)
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
TPA.Global.full.BeastTree.ukmeta

#ggsave(paste0(Figure_output_directory,"SupFig7_TPA_FullBeastTree.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=240, device='pdf', dpi=1200)
Now do sublineage plots
Make some plots
# Sublineage 1
sublineage.1.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.2)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Sublineage.2
sublineage.2.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 2), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Sublineage.8
sublineage.8.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 8), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Sublineage.14
sublineage.14.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Plot together?
Maybe with sublineage 1 expanded?
p.beast.trees.heatmap.sublineages.combi.offset1 <- plot_grid(sublineage.2.tree.heatmap,
sublineage.8.tree.heatmap,
sublineage.14.tree.heatmap,
ncol=2, labels=c("B - Sublineage 2","C - Sublineage 8","D - Sublineage 14"), label_size=panel.lab.size, scale=0.95, vjust=1.0)
p.beast.trees.heatmap.sublineages.combi.offset2 <- plot_grid(sublineage.1.tree.heatmap, p.beast.trees.heatmap.sublineages.combi.offset1, labels=c("A - Sublineage 1", ""), label_size=panel.lab.size, scale=0.975, ncol=2, rel_widths=c(6,11), vjust=2.5)
p.beast.trees.heatmap.sublineages.combi.offset2

#ggsave(paste0(Figure_output_directory,"SupFig8_TPA-PHE_Sublineage-BeastTrees.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=265, height=230, device='pdf', dpi=1200)
Need to explore sublineage 14 a bit more to get dates for those subclades
sublineage.14.tree.heatmap + geom_tiplab(size=theme.text.size.within, linesize=0.4) #3

# Ok, there are multiple subclades in this tree
sublineage.14.tree.heatmap.data <- sublineage.14.tree.heatmap$data
# getMRCA(full.beast2.tree@phylo,c("PHE150150A","NL14","TPA_BCC122","TPA_BCC126","PHE140076A","TPA_UKBRG008")) 982
# full.beast2.tree@phylo$tip.label[phangorn::Descendants(full.beast2.tree@phylo, 982, type = c("tips"))[[1]]]
sublineage.14.lowerclade.list <- c("NL17", "NL19", "PHE140085A", "PHE140089A", "PHE150118A", "PHE150121A", "PHE150133A", "PHE150143A", "PHE150145A", "PHE150162A", "PHE150166A", "PHE150168A", "PHE160224A", "PHE160243A", "PHE160255A", "PHE160276A", "PHE160290A", "PHE160302A", "PHE160306A", "PHE170333A", "PHE170349A", "PHE170374A", "PHE170381A", "PHE170664A", "TPA_ESBCN005", "TPA_UKBIR032")
sublineage.14.upperclade.list <- c("NL14", "PHE140076A", "PHE150149A", "PHE150150A", "PHE150170A", "PHE160196A", "PHE160263A", "PHE160274A", "PHE160287A", "PHE160294A", "PHE160316A", "PHE160317A", "PHE170372A", "PHE170386A", "PHE170397A", "PHE170405A", "TPA_BCC081", "TPA_BCC088", "TPA_BCC089", "TPA_BCC101", "TPA_BCC122", "TPA_BCC126", "TPA_BCC136", "TPA_BCC169", "TPA_HUN180004", "TPA_HUN190020", "TPA_UKBIR044", "TPA_UKBRG007", "TPA_UKBRG008")
# Get MRCA date for lower clade
sublineage.14.lowerclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.lowerclade.list),"x"]
paste0("TMRCA for sublineage 14 lower clade: ",sublineage.14.lowerclade.list.tmrca)
[1] "TMRCA for sublineage 14 lower clade: 2006.53850498154"
# Get MRCA date for upper clade
sublineage.14.upperclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.upperclade.list),"x"]
paste0("TMRCA for sublineage 14 upper clade: ",sublineage.14.upperclade.list.tmrca)
[1] "TMRCA for sublineage 14 upper clade: 1999.15025243934"
Extract key information for sublineage 6 (two samples)
sublineage.6.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
sublineage.6.tree.heatmap.data <- sublineage.6.tree.heatmap$data
# Get MRCA date for upper clade
sublineage.6.beasttree.tmrca <- as.numeric(sublineage.6.tree.heatmap.data[sublineage.6.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6)@phylo, c("PHE130048A", "PHE160283A")),"branch"])
paste0("TMRCA for sublineage 6 upper clade: ",sublineage.6.beasttree.tmrca)
[1] "TMRCA for sublineage 6 upper clade: 1982.61865062176"
### Extract sample & population statistics from datasets for use in manuscript text
Dataset and Geographical distributions
# dataset counts
paste0("Total UK samples in cleaned/deduplicated dataset: ",nrow(PHE.metadata.linked))
[1] "Total UK samples in cleaned/deduplicated dataset: 237"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="PHE",])," from PHE Ref lab at Colindale")
[1] "Of which: 195 from PHE Ref lab at Colindale"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="Other",])," from other labs")
[1] "Of which: 42 from other labs"
# proportion with geographical data
paste0("From UK samples, ", nrow(PHE.metadata.linked[(PHE.metadata.linked$phe_centre %notin% c("Not Known","UK (not England)")),])," were grouped into one of the 9 PH regions")
[1] "From UK samples, 217 were grouped into one of the 9 PH regions"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="UK (not England)",]), " were referred from outside England")
[1] "From UK samples, 2 were referred from outside England"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="Not Known",]), " had unknown region")
[1] "From UK samples, 18 had unknown region"
# counts & fractions by PHE region
PHE.geo.count
NA
Gender Orientation stats
PHE.orientation.counts
PHE.geo.orientation.counts
PHE.geo.HIV.counts
PHE.sublineage.orientation.counts
PHE.sublineage.Age
Sublineage Distributions
PHE.Lineage.count
PHE.sublin.count
PHE.geo.sublineage
Macrolide resistance stats
UK.macrolide.res <- PHE.metadata.linked %>%
dplyr::group_by(A2058G, A2059G) %>%
dplyr::summarise(Count.allele=n()) %>%
dplyr::ungroup() %>%
dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res
UK.macrolide.res.sublin <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, A2058G, A2059G) %>%
dplyr::summarise(Count.allele=n()) %>%
dplyr::ungroup() %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res.sublin
# Calculate long form df, with different 23S alleles (A2058G, A2059G, WT, Uncertain) v.s. sublineage
UK.macrolide.res.sublin.long <- PHE.metadata.linked %>%
mutate(Resistance.allele=ifelse(A2058G=="Yes", "A2058G", ifelse(A2059G=="Yes", "A2059G", ifelse((A2058G=="No" & A2059G=="No"),"Wild Type", "Uncertain")))) %>%
dplyr::group_by(TPA.pinecone.sublineage, Resistance.allele) %>%
dplyr::summarise(Count.per.sublin.Macrolides=n()) %>%
dplyr::mutate(total.sublin=sum(Count.per.sublin.Macrolides),
fraction=Count.per.sublin.Macrolides/total.sublin) %>%
#dplyr::ungroup() %>%
dplyr::arrange((Resistance.allele), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Resistance.allele = factor(Resistance.allele, levels=rev(c("A2058G", "A2059G", "Uncertain", "Wild Type"))))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Make plot of macrolide resistance by sublineages
p.sublin.Macrolides.hbarplot <- ggplot(UK.macrolide.res.sublin.long, aes(Count.per.sublin.Macrolides, y=TPA.pinecone.sublineage, fill=Resistance.allele)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
scale_fill_manual(name="Macrolide\nResistance\nAllele",values=c("indianred2", "steelblue1","grey55", "grey90"), breaks=c("A2058G", "A2059G", "Uncertain", "Wild Type")) +
labs(y="TPA Sublineage", x="Proportion with Macrolide Resistance Allele") +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
guides(fill=guide_legend(ncol=2)) +
geom_text(data=UK.macrolide.res.sublin.long, aes(cum_fract.mid, y=TPA.pinecone.sublineage,label=Count.per.sublin.Macrolides), size=theme.text.size.within, inherit.aes = F) +
NULL
p.sublin.Macrolides.hbarplot

# Combine plot with sublineage count bars
p.sublin.Macrolides.hbarplot.combi <- plot_grid(p.sublineage.hbarplot + guides(fill=guide_legend(ncol=3)), p.sublin.Macrolides.hbarplot + y.theme.strip, nrow=1, align=T, labels=c("A", "B"), label_size=panel.lab.size)
p.sublin.Macrolides.hbarplot.combi

#ggsave(paste0(Figure_output_directory,"SupFig9_TPA-PHE_Sublin-Macrolide-Res.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=160, height=120, device='pdf', dpi=1200)
Pairwise SNP analysis
OK, want to investigate the different patterns observable for the North East of England (pale blue) in Sublineage 1
Multiple ways we can do this - including SNP distances (also multiple ways to do that)
###
#Use phylogenetic distance from the SNP scaled tree
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist <- ape::cophenetic.phylo(TPA.pyjar.tree.subset.uk)
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt <- data.frame(Taxa1=row.names(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist), TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist, stringsAsFactors = F) %>% tidyr::gather(Taxa2, Distance.Phylo, -Taxa1)
# Taxa Comparisons label
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa_combination <- sapply(1:nrow(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt), function (x) paste0(sort(c(as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa1[x]),as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa2[x]))),collapse="___"))
# Merge together
#TPA.WGS.alignment.data.dist.melt <- dplyr::left_join(TPA.WGS.alignment.data.dist.melt, TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt[,c("Taxa_combination","Distance.Phylo")], by="Taxa_combination")
TPA.WGS.alignment.data.dist.melt <- TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt
TPA.WGS.alignment.data.dist.melt <- unique(TPA.WGS.alignment.data.dist.melt)
Ok, now bring in some metadata and comparisons
# Bring in and merge metadata
PHE.meta.pairwise.t1 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t1) <- paste0(colnames(PHE.meta.pairwise.t1),".t1")
colnames(PHE.meta.pairwise.t1)[1] <- "Taxa1"
PHE.meta.pairwise.t2 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t2) <- paste0(colnames(PHE.meta.pairwise.t2),".t2")
colnames(PHE.meta.pairwise.t2)[1] <- "Taxa2"
PHE.alignment.data.dist.melt.meta <- plyr::join(TPA.WGS.alignment.data.dist.melt,PHE.meta.pairwise.t1, by="Taxa1", type="left")
PHE.alignment.data.dist.melt.meta <- plyr::join(PHE.alignment.data.dist.melt.meta,PHE.meta.pairwise.t2, by="Taxa2", type="left")
# Exclude missing data (e.g. missing sublineage) - this will also remove non-UK samples, since full metadata is missing here
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1),]
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2),]
Define comparisons
# Same sample
PHE.alignment.data.dist.melt.meta$same.sample <- ifelse(PHE.alignment.data.dist.melt.meta$Taxa1==PHE.alignment.data.dist.melt.meta$Taxa2,"same", "different")
# Years between samples
PHE.alignment.data.dist.melt.meta$year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$year.t2))
PHE.alignment.data.dist.melt.meta$Sample_Year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t2))
# Years between decimal date (more precise temporal distance)
PHE.alignment.data.dist.melt.meta$decimal.date.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t2))
# Epidemiological time between - catagorical
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<1/12,"month", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3/12, "quarter", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6/12, "half year", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "1 year",ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "2 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "3 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "4 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "5 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "6 years",">6 years")))))))))
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- factor(PHE.alignment.data.dist.melt.meta$epi.time.distance.cat, levels=c("month", "quarter","half year","1 year", "2 years", "3 years", "4 years", "5 years", "6 years", ">6 years"))
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat.years <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "0", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "1", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "2", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "3", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "4", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "5",">5"))))))
# Same country
PHE.alignment.data.dist.melt.meta$same.country <- ifelse(PHE.alignment.data.dist.melt.meta$Geo_Country.t1 == PHE.alignment.data.dist.melt.meta$Geo_Country.t2, "same", "different")
# Is UK
PHE.alignment.data.dist.melt.meta$both.uk <- ifelse(PHE.alignment.data.dist.melt.meta$is.UK.t1 == PHE.alignment.data.dist.melt.meta$is.UK.t2, "same", "different")
# Is PHE
PHE.alignment.data.dist.melt.meta$both.PHE <- ifelse(PHE.alignment.data.dist.melt.meta$is.PHE.t1 == PHE.alignment.data.dist.melt.meta$is.PHE.t2, "same", "different")
# Same TPA Lineage (cleaned up classifications)
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- ifelse(PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1==PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse((PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1[x]=="0" | PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2[x]=="0"),NA,PHE.alignment.data.dist.melt.meta$same.TPA.Lineage[x]))
# Same TPA sublineage
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- ifelse(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1==PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2,"same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse(((PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1[x]=="Singleton") |(PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2[x]=="Singleton")),"different",PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster[x]))
# Define Genetic relationships hierarchically
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy <- ifelse(PHE.alignment.data.dist.melt.meta$Distance==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy.ph <- ifelse(PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))
# Same PHE region
PHE.alignment.data.dist.melt.meta$same.PHE.region <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1==PHE.alignment.data.dist.melt.meta$phe_centre.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$PHE.centre.combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t2[x]))),collapse="___"))
# does the combination included London?
PHE.alignment.data.dist.melt.meta$involves.London <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="London" | PHE.alignment.data.dist.melt.meta$phe_centre.t2=="London", "London", "not-London")
# Orientation pair
PHE.alignment.data.dist.melt.meta$Orientation_combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]))),collapse="___"))
#PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "MSM",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" | PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "Mixed",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSW" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="WSM","Heterosexual",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="WSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSW","Heterosexual","Unknown")))))
PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="GBMSM", "GBMSM",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"),"Heterosexual",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"), "Mixed",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM", "Mixed", "Unknown")))))
# Country Comparisons label
PHE.alignment.data.dist.melt.meta$Country_combinations <- paste0(PHE.alignment.data.dist.melt.meta$Geo_Country.t1,"___",PHE.alignment.data.dist.melt.meta$Geo_Country.t2)
# Subset to PHE data only (effectively already done, but let's be explicit)
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same" & PHE.alignment.data.dist.melt.meta$both.PHE=="same"),]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[PHE.TPA.alignment.data.dist.melt.meta$PHE.only=="PHE",]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same"),]
# Make single sided
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[!duplicated(PHE.TPA.alignment.data.dist.melt.meta$Taxa_combination),]
### Perform a more detailed analysis of samples from the North East of England
Do a more detailed exploration of the North East of England
PHE.metadata.linked2.region_NorthEast <- PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East",]
# Constrain by samples being from the North East
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="North East" & PHE.alignment.data.dist.melt.meta$same.sample=="different"),]
# Constrain by the same PHE region
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$same.PHE.region=="same",]
#Just plot these distros
p.NorthEast.Pairwise.SNPs.unconstrained <- ggplot(PHE.alignment.data.dist.melt.meta.NorthEast.clusters, aes(Distance.Phylo)) +
geom_histogram(binwidth = 1) +
theme_bw() +
theme.text.size +
labs(x="Pairwise SNP Distance", y="Comparison Count")
p.NorthEast.Pairwise.SNPs.unconstrained

Make a single linkage network from the North East samples
# Constrain by SNP distance (looser than previously - we just want to find basic groupings within sublineage 1 for NE samples)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo<=2,]
# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo),]
# cleanup some data noise
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$year.t1),]
# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat")]
############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[!duplicated(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename),]
# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2)
############
#inverse weight
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.inv <- 1/PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.Phylo
# Make actual network
set.seed(1235)
PHE.NorthEast.network <- network(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F)
PHE.NorthEast.network.gg <- ggnetwork(PHE.NorthEast.network, layout = "kamadakawai", weights = "Distance.inv")
PHE.NorthEast.network.gg$Taxa1 <- PHE.NorthEast.network.gg$vertex.names
# extract temporal clusters from network
PHE.NorthEast.network.ig <- asIgraph(PHE.NorthEast.network)
PHE.NorthEast.network.components <- data.frame(Taxa1=network.vertex.names(PHE.NorthEast.network), vertex.no=as.vector(V(PHE.NorthEast.network.ig)), cluster=igraph::components(PHE.NorthEast.network.ig)$membership)
# For ease of story telling in the paper, flip clusters 2 and 3 around (so we can talk about 2 first)
PHE.NorthEast.network.components <- PHE.NorthEast.network.components %>%
dplyr::mutate(cluster.old=cluster, cluster=ifelse(cluster.old==2, 3, ifelse(cluster.old==3,2,cluster.old)))
PHE.NorthEast.network.components$Cluster <- paste0("Cluster",PHE.NorthEast.network.components$cluster)
# merge metadata back in
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.NorthEast.network.components$Taxa1, Cluster=PHE.NorthEast.network.components$Cluster), by="Taxa1", type="left")
Plot network
# Plot network
p.PHE.NorthEast.network.2SNP <- ggplot(PHE.NorthEast.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(Distance.Phylo), linetype=factor(Distance.Phylo))) +
scale_color_manual(values=c("grey5","grey55","grey85"), name="SNP\nDistance") +
scale_linetype(name="SNP\nDistance") +
theme_blank() +
ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
geom_nodes(size=1.0, aes(color=gender_orientation)) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
NULL
p.PHE.NorthEast.network.2SNP

Ok, so three networks. Clear differentiation of a heterosexual network (with 0-snp distances) and two predominantly MSM networks
Let’s look at the phylogenetic context of those North East clusters we’ve defined. Pull out subtrees (from sublineage 1 subtree)
# Cluster 1
Beast.tree.NE.cluster1 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster1","Taxa1"])
Beast.tree.NE.cluster1.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster1, levels_back=0)
p.Beast.tree.NE.cluster1.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster1.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 10)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Can't fit in tip labs, but since this is a polyphyletic subtree, it would be helpful to add a track to highlight the NE strains
PHE.metadata.linked$is.NorthEast <- ifelse(PHE.metadata.linked$phe_centre=="North East","North East", "Other England")
p.Beast.tree.NE.cluster1.subtree.cluster.highlight <- gheatmap(p.Beast.tree.NE.cluster1.subtree, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- gheatmap(p.Beast.tree.NE.cluster1.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*6),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID +
coord_cartesian(x=c(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))+2)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#######################
# Cluster 2
Beast.tree.NE.cluster2 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster2","Taxa1"])
Beast.tree.NE.cluster2.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster2, levels_back=1)
p.Beast.tree.NE.cluster2.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster2.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 20) + geom_tiplab(size=theme.text.size.within, align=T, offset=5, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster2.subtree <- gheatmap(p.Beast.tree.NE.cluster2.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster2.subtree$data$height)*3), offset=20+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster2.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster2.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster2.subtree <- p.Beast.tree.NE.cluster2.subtree +
coord_cartesian(x=c(p.Beast.tree.NE.cluster2.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster2.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))+0.5)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster2.subtree
############################
# Cluster 3
Beast.tree.NE.cluster3 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster3","Taxa1"])
Beast.tree.NE.cluster3.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster3, levels_back=1)
p.Beast.tree.NE.cluster3.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster3.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 26) + geom_tiplab(size=theme.text.size.within, align=T, offset=3, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster3.subtree <- gheatmap(p.Beast.tree.NE.cluster3.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster3.subtree$data$height)*3), offset=26+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster3.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster3.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster3.subtree <- p.Beast.tree.NE.cluster3.subtree +
coord_cartesian(x=c(p.Beast.tree.NE.cluster3.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster3.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))+0.5)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster3.subtree
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#p.Beast.tree.NE.cluster2.subtree
#p.Beast.tree.NE.cluster3.subtree
Since Cluster 1 is really quite polyphyletic, it maybe more useful to show the clusters in context for that one
# Add North East identifier column
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(sublineage.1.tree.heatmap, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(sublineage.1.tree.heatmap$data$height)*3)*1.2, offset=0+(4*5)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$height)*3)*1.2, offset=0+(4*6)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight +
coord_cartesian(x=c(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))+2))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# reduce spacing between legend scales
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + theme(legend.margin = margin(-0.95,0,0,0, unit="mm"))
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight

Plot together
p.Beast.tree.NE.subtrees.combi1 <- plot_grid(p.Beast.tree.NE.cluster2.subtree, p.Beast.tree.NE.cluster3.subtree, ncol=1, labels=c("C - Cluster 2", "D - Cluster 3"), vjust=1.0, label_size=panel.lab.size, scale=0.95)
p.Beast.tree.NE.subtrees.combi2 <- plot_grid(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(3,2), labels=c("B - Cluster 1", ""), label_size=panel.lab.size)
p.Beast.tree.NE.subtrees.combi2

p.Beast.tree.NE.subtrees.combi3 <- plot_grid(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(8,7), labels=c("B - Sublineage 1 (All)", ""), label_size=panel.lab.size, scale=0.95, vjust=1.0)
p.Beast.tree.NE.subtrees.combi3

Look more closely at population demographics of these clusters
# Metadata on NE cluster 2
PHE.metadata.linked %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster2.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Metadata on NE cluster 3
PHE.metadata.linked %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Country info on NE cluster 3
TPA.meta2.1 %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country) %>%
dplyr::summarise(Count=n())
# Separate metadata records show Hungarian sample "TPA_HUN180001" came from a male bisexual (MSWM).
Examine SNP scaled tree for distances
# Extract information about SNP distances
TPA.NEcluster3.pyjartree.mrca <- getMRCA(TPA.pyjar.tree, as.character(unlist(TPA.meta2.1[TPA.meta2.1$Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label,"Sample_Name"])))
TPA.NEcluster3.pyjartree.subtree <- tree_subset(TPA.pyjar.tree, node=TPA.NEcluster3.pyjartree.mrca, levels_back=1)
ggtree(TPA.NEcluster3.pyjartree.subtree) + geom_tiplab(size=theme.text.size.within)

ggtree(TPA.NEcluster3.pyjartree.subtree)$data
Do some analysis of nearest neighbour and distances to MRCAs
calculate.years.from.mrca <- function(current.ggtree.phylo, current.ggtree.data){
#current.ggtree <- Beast.tree.NE.cluster3.subtree
all.tips <- current.ggtree.phylo$tip.label
dist.2.mrca <- NULL
### put dates into df
current.ggtree.data$mrca.median <- 2019.5 - current.ggtree.data$height_median
current.ggtree.data$year <- as.numeric(round(2019.5 - current.ggtree.data$height_median,3))
current.ggtree.data$mrca.95high <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[1]), 3)
current.ggtree.data$mrca.95low <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[2]), 3)
# extract dates between sample and its MRCA using loop
for (current.node in all.tips) {
current.parent <- c(match(current.node,current.ggtree.phylo$tip.label), phangorn::Ancestors(current.ggtree.phylo, match(c(current.node), current.ggtree.phylo$tip.label), "parent"))
current.nodelist <- current.ggtree.data[current.ggtree.data$node %in% current.parent,]
current.dist.2.mrca <- c(current.node, as.numeric(current.nodelist[1,"year"]-current.nodelist[2,"year"]))
dist.2.mrca <- rbind(dist.2.mrca, current.dist.2.mrca)
}
dist.2.mrca <- data.frame(Sample_Name=as.character(dist.2.mrca[,1]), dist.to.mrca=as.numeric(dist.2.mrca[,2]), stringsAsFactors=F)
return(dist.2.mrca)
}
### All samples in global tree
dist.mrca.all.TPA <- calculate.years.from.mrca(full.beast2.tree@phylo, full.beast2.tree@data)
Merge dist2MRCA with metadata
PHE.metadata.linked.dist2mrca <- left_join(PHE.metadata.linked, dist.mrca.all.TPA, by="Sample_Name")
p.time2mrca.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(gender_orientation, dist.to.mrca, color=gender_orientation)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="Gender Orientation", y="Years to MRCA", color="Gender Orientation") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=phe_centre)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip(ylim=c(0,40)) +
labs(x="UKHSA Region", y="Years to MRCA", color="UKHSA Region") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region)
p.time2mrca.phe_region.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=gender_orientation)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip(ylim=c(0,20)) +
labs(x="UKHSA Region", y="Years to MRCA") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region.orientation
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.sublineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA.pinecone.sublineage, dist.to.mrca, color=TPA.pinecone.sublineage)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="TPA Lineage", y="Years to MRCA", color="TPA Lineage") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
p.time2mrca.sublineage
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.Lineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA_Lineage, dist.to.mrca, color=TPA_Lineage)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="TPA Lineage", y="Years to MRCA (Median of Posterior)", color="TPA Lineage") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage)
Maybe can make an MST of the North East samples for grapetree?
TPA.pyjar.tree.subset.NorthEast <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East","Sample_Name"])))
#ggtree(TPA.pyjar.tree.subset.NorthEast)
#write.tree(TPA.pyjar.tree.subset.NorthEast, paste0(Data_input_directory,"TPA.UK-only-NorthEast.pyjar.2022-02-26.tre"))
# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"
#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)
Alternative approach using MST instead of networks for North East data
# Read in MST
#TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")
p.TPA.NorthEastEngland.Grapetree <- ggdraw() + draw_image(TPA.NorthEastEngland.Grapetree.file)
p.TPA.NorthEastEngland.Grapetree

p.TPA.NorthEastEngland.Grapetree.header <- plot_grid(p.TPA.NorthEastEngland.Grapetree, labels=c("A - Network Clusters (North East England)"), label_size=panel.lab.size, scale=0.95)
Plot with beast trees
#p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,6), labels=c("A - Network Clusters (North East England)", ""), label_size=panel.lab.size, scale = 0.95)
p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree.header, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,7))
p.PHE.NorthEast_MST.with.beast.subtrees.combi

#ggsave(paste0(Figure_output_directory,"Fig3_Sublin1.NorthEast.MST+Beast.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=200, height=245, device='pdf', dpi=1200)
Do some analysis of major sublineages over time by region - could this influence observations about sublineages?
# Generate some stats by PHE Region
PHE.major.sublineage.PHEcentre.date <- PHE.metadata.linked %>%
dplyr::filter(TPA.pinecone.sublineage %in% c(1,14)) %>%
dplyr::group_by(TPA.pinecone.sublineage, phe_centre, year) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(phe_centre), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'phe_centre'. You can override using the `.groups` argument.
ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, phe_centre, size=Count, color=TPA.pinecone.sublineage)) +
geom_point() +
facet_grid(.~TPA.pinecone.sublineage) +
theme_light() +
theme.text.size +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)

p.PHE.major.sublineage.PHEcentre.date.bubbleplot <- ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, TPA.pinecone.sublineage, color=TPA.pinecone.sublineage)) +
geom_point(alpha=0.65, aes(size=Count)) +
geom_line(alpha=0.25) +
facet_grid(factor(gsub("\\ ","\n",phe_centre), levels=gsub("\\ ","\n",PHE.region.cols.brew$UKHSA.region))~., switch='y') +
theme_light() +
theme(strip.placement = "outside") +
theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y=element_text(color = "grey25",angle=0, size=5)) +
scale_size_area(max_size = 4.5,breaks=c(1,5,10,20,30,40)) +
theme.text.size +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="Region", x="Year", color="Sublineage")
p.PHE.major.sublineage.PHEcentre.date.bubbleplot
geom_path: Each group consists of only one observation. Do you need to adjust the group aesthetic?

Do some specific analysis for the 3 Northern regions
# Generate some stats by PHE Region
PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::summarise(count=n())
PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(year) %>%
dplyr::summarise(count=n())
p.PHE.major.sublineage.3NorthernRegions <- PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(TPA.pinecone.sublineage, year, phe_centre) %>%
dplyr::summarise(Count=n()) %>%
ggplot(aes(year, Count, fill=phe_centre)) +
geom_bar(stat='identity', width=0.65) +
scale_fill_manual(values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
theme_bw() + theme.text.size +
scale_x_continuous(breaks=seq(2012,2018,1)) +
scale_y_continuous(breaks=pretty) +
labs(title="Samples in 3 Northern Regions", x="Collection Year", y="Sample Count", fill="Public Health\nRegion") +
theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
#geom_text(aes(x=year,y=Count-0.5, label=Count), color='grey95', size=theme.text.size.within) +
NULL
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'year'. You can override using the `.groups` argument.
p.PHE.major.sublineage.3NorthernRegions

Single linkage network of identical genomes from UK
# Constrain by SNP distance (identical in the asr snp tree)
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta[PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,]
# and a max of 2 years
#PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$decimal.date.distance<=2,]
# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$Distance.Phylo),]
# remove self-samples
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$same.sample=="different",]
# cleanup some data noise
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$year.t1),]
# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat.years","epi.time.distance.cat")]
############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.identicals.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.identicals.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.identicals.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals.input1[!duplicated(PHE.alignment.data.dist.melt.meta.identicals.input1$edgename),]
# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2)
############
# Deduplicate
#inverse weight
PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance.inv <- 1/1/(PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance+0.04)
# Make actual network
set.seed(1236)
PHE.identicals.network <- network(PHE.alignment.data.dist.melt.meta.identicals.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F, loops = F)
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "kamadakawai", weights = "decimal.date.distance.inv")
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold", weights = "decimal.date.distance")
PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold")
PHE.identicals.network.gg$Taxa1 <- PHE.identicals.network.gg$vertex.names
# extract temporal clusters from network
PHE.identicals.network.ig <- asIgraph(PHE.identicals.network)
PHE.identicals.network.components <- data.frame(Taxa1=network.vertex.names(PHE.identicals.network), vertex.no=as.vector(V(PHE.identicals.network.ig)), cluster=igraph::components(PHE.identicals.network.ig)$membership)
PHE.identicals.network.components$Cluster <- paste0("Cluster",PHE.identicals.network.components$cluster)
# merge metadata back in
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster), by="Taxa1", type="left")
#
# Add temporal colour scale
#unique(PHE.identicals.network.gg$epi.time.distance.cat)
epi.time.distance.cat.cols <- rev(colorRampPalette(brewer.pal(8, "Greys"))(length(unique(PHE.identicals.network.gg$epi.time.distance.cat))-1))
# Plot network
p.PHE.identicals.network.0SNP <- ggplot(PHE.identicals.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(epi.time.distance.cat), linetype=factor(epi.time.distance.cat))) +
#scale_color_manual(values=c("grey5","grey35","grey55", "grey65", "grey75"), name="SNP\nDistance") +
scale_color_manual(name="Temporal\nDistance", values = epi.time.distance.cat.cols) +
scale_linetype(name="Temporal\nDistance") +
theme_blank() +
ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
#geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
geom_nodes(size=2.5, aes(color=gender_orientation), alpha=0.9) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
NULL
p.PHE.identicals.network.0SNP

Plot this against a UK tree?
gheatmap(ggtree(TPA.pyjar.tree.subset.uk),
data.frame(row.names=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster))

Some stats from this
p.PHE.identical.Orientation_class.bydatedist <- PHE.alignment.data.dist.melt.meta %>%
dplyr::filter(same.sample=="different", Distance.Phylo==0) %>%
#filter(decimal.date.distance<=1) %>%
dplyr::group_by(epi.time.distance.cat, Orientation.Class) %>%
dplyr::summarise(Count.class.date=n()) %>%
dplyr::mutate(sum.class=sum(Count.class.date), fract.class=Count.class.date/sum.class) %>%
ggplot(aes(x=epi.time.distance.cat, y=Count.class.date, fill=Orientation.Class)) +
geom_bar(stat='identity', position='stack') +
theme_bw() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Time between samples", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'epi.time.distance.cat'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.bydatedist

p.PHE.identical.Orientation_class.byZerodist.cluster <- PHE.identicals.network.gg %>%
dplyr::filter(!is.na(Orientation.Class)) %>%
dplyr::group_by(Cluster, Orientation.Class) %>%
dplyr::summarise(Count.class.cluster=n()) %>%
dplyr::mutate(sum.class=sum(Count.class.cluster), fract.class=Count.class.cluster/sum.class) %>%
dplyr::arrange(desc(sum.class)) %>%
dplyr::ungroup() %>%
dplyr::mutate(Cluster=as_factor(Cluster)) %>%
ggplot(aes(x=Cluster, y=Count.class.cluster, fill=Orientation.Class)) +
geom_bar(stat='identity', position='stack') +
theme_bw() +
x.theme.axis.rotate +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Identical Genome Cluster", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.byZerodist.cluster

d.PHE.identical.GenderOrientation.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
dplyr::group_by(TPA.pinecone.sublineage, Cluster, gender_orientation) %>%
dplyr::summarise(count.orient.cluster=n()) %>%
dplyr::mutate(count.cluster=sum(count.orient.cluster), fract=count.orient.cluster/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(count.cluster)) %>%
dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
# Plot sample counts by genome cluster (coloured by orientation)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
ggplot(aes(Cluster.o, count.orient.cluster, fill=gender_orientation)) +
geom_bar(stat="identity", width=0.65) +
scale_fill_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation, guide = guide_legend(order = 1)) +
theme_light() +
x.theme.axis.rotate +
scale_y_continuous(breaks=seq(0,45,5)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Identical Genome Cluster", y="Sample Count", fill="Patient Gender Orientation")
# Add details of sublineage
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster +
ggnewscale::new_scale_color() +
geom_point(data=(d.PHE.identical.GenderOrientation.byZerodist.cluster %>% select(Cluster.o, TPA.pinecone.sublineage) %>% distinct()), aes(Cluster.o, -1.5, color=TPA.pinecone.sublineage), inherit.aes = F) + scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, name="Sublineage", guide = guide_legend(order = 2)) +
NULL
# Add a sublineage axis label (bit of a hack)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster +
geom_text(data=data.frame(lab="Sublineage", y=-1.5, x=28, stringsAsFactors=F), aes(label=lab, x=x, y=y), hjust = 0.1, size=theme.text.size.within, inherit.aes = F) +
coord_cartesian(x=c(1, 27), clip='off')
p.PHE.identical.GenderOrientation.byZerodist.cluster

#ggsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=100, device='pdf', dpi=1200)
Possible to introduce some more info into that plot?
d.PHE.identical.region.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
dplyr::group_by(TPA.pinecone.sublineage, Cluster, phe_centre) %>%
dplyr::summarise(count.region.cluster=n()) %>%
dplyr::mutate(count.cluster=sum(count.region.cluster), fract=count.region.cluster/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(count.cluster)) %>%
dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Region.byZerodist.cluster <- d.PHE.identical.region.byZerodist.cluster %>%
ggplot(aes(Cluster.o, count.region.cluster, fill=phe_centre)) +
geom_bar(stat="identity", width=0.65, position='fill') +
scale_fill_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, guide = guide_legend(order = 1)) +
theme_light() +
x.theme.axis.rotate +
scale_y_continuous(breaks=seq(0,45,5)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
guides(fill=guide_legend(ncol=2)) +
labs(x="Identical Genome Cluster", y="Region Proportion", fill="UKHSA Region")

PHE.identicals.network.gg.region.scatterpie.groups <- PHE.identicals.network.gg %>%
dplyr::select(Cluster, Taxa1, phe_centre) %>%
dplyr::distinct() %>%
dplyr::group_by(Cluster, phe_centre) %>%
dplyr::summarise(Count.centre=n()) %>%
dplyr::mutate(x=Cluster, y=3.5) %>%
pivot_wider(names_from="phe_centre", values_from="Count.centre", values_fill=0) %>%
dplyr::select(Cluster,x,y,unique(PHE.identicals.network.gg$phe_centre)) %>%
dplyr::ungroup() %>%
dplyr::mutate(Cluster.numeric=as.numeric(1:27))
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.GenderOrientation.byZerodist.cluster +
ggnewscale::new_scale_fill() #+

NA
NA
Get a few more stats on the largest cluster (Cluster 8)
#d.PHE.identical.GenderOrientation.byZerodist.cluster %>% filter(Cluster=="Cluster8")
PHE.identicals.network.gg.identical.cluster8 <- PHE.identicals.network.gg %>% filter(Cluster=="Cluster8") %>%
select(vertex.names, Orientation.Class, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos, Cluster)
sort(unique(PHE.identicals.network.gg.identical.cluster8$year))
[1] 2012 2013 2014 2015 2016 2017 2018
Get some more information about the heterosexual only clusters
PHE.identicals.network.gg.identical_heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %in% c("Cluster12", "Cluster20", "Cluster27")) %>%
select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>%
distinct() %>%
arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_heteroclusters
And do the same for the small mixed/GBMSM clusters
PHE.identicals.network.gg.identical_not.heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %notin% c("Cluster12", "Cluster20", "Cluster27", "Cluster8")) %>%
select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>%
distinct() %>%
arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_not.heteroclusters
What proportion of heterosexuals have an identical GBMSM paired genome?
# Delineate heterosexual clusters
d.PHE.identical.heterosexual.clusters <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
dplyr::mutate(is.heterosexual=ifelse(gender_orientation%in% c("MSW", "WSM"), "heterosexual", ifelse(gender_orientation=="GBMSM","GBMSM", "Unknown"))) %>%
dplyr::group_by(Cluster,is.heterosexual) %>%
dplyr::mutate(count.hetero=sum(count.orient.cluster), fract.hetero=sum(count.orient.cluster)/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::filter(is.heterosexual=="heterosexual") %>%
dplyr::select(-c(count.orient.cluster, gender_orientation, fract)) %>%
dplyr::distinct() %>%
dplyr::mutate(cluster.type=ifelse(fract.hetero==1, "hetero.only", "other"))
d.PHE.identical.heterosexual.clusters
# What proportion of heterosexuals (n=20) are in a heterosexual-only cluster?
d.PHE.identical.heterosexual.clusters %>%
dplyr::group_by(cluster.type) %>%
dplyr::summarise(count.in.hetero.cluster=sum(count.hetero)) %>%
dplyr::mutate(fract.in.hetero=count.in.hetero.cluster/sum(count.in.hetero.cluster))
#left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name"))
Revisions 03-2023 onwards
Look at proportion of genomes at different coverage thresholds
# Cumulative proportion of N counts in genomes
PHE.metadata.Ncount.cummulative.UK <- PHE.metadata.linked %>%
dplyr::filter(is.UK=="UK") %>%
dplyr::group_by(`Proportion-N_>5_mapping+masking_Nichols`) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.Count=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.Count, cum_fract=cumsum(fraction), cum_count=cumsum(Count)) %>%
dplyr::mutate(Dataset="UK (n=237)")
PHE.metadata.Ncount.cummulative.UK
PHE.metadata.Ncount.cummulative.ALL <- TPA.meta2.1 %>%
dplyr::filter(full.temporal.analysis=="Yes") %>%
dplyr::group_by(`Proportion-N_>5_mapping+masking_Nichols`) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.Count=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.Count, cum_fract=cumsum(fraction), cum_count=cumsum(Count)) %>%
dplyr::mutate(Dataset="All (n=520)")
PHE.metadata.Ncount.cummulative.ALL
PHE.metadata.Ncount.cummulative.combi <- rbind(PHE.metadata.Ncount.cummulative.UK, PHE.metadata.Ncount.cummulative.ALL)
p.cumulative.Ncount.for.datset <- ggplot(PHE.metadata.Ncount.cummulative.combi , aes(`Proportion-N_>5_mapping+masking_Nichols`, cum_fract, group=Dataset, color=Dataset)) +
geom_point(alpha=0.75, size=1) +
theme_light() +
theme.text.size + theme(legend.position = 'top') +
labs(y="Cumulative fraction of genomes", x="Proportion of sites masked to N") +
scale_y_continuous(breaks=seq(0,1,0.1))
p.cumulative.Ncount.for.datset

BEAST 95% HPD calculations (provide more details for 520 dataset )
BEAST.median <- 1.28e-7
BEAST.95HPD <- c(1.07e-7, 1.48e-7)
SS14.aln.length <- 1139569
1/(BEAST.median * SS14.aln.length)
[1] 6.855662
1/(BEAST.95HPD * SS14.aln.length)
[1] 8.201166 5.929221
Further evaluation of sublineage 6 (reviewer response) using ancestral reconstruction performed on the global TPA-only alignment/tree used in Beale 2021.
TPA.treetime.ancestral.tree <- read.nexus(TPA.treetime.ancestral.tree.file)
TPA.treetime.ancestral.tree.data <- fortify(TPA.treetime.ancestral.tree)
ggtree(TPA.treetime.ancestral.tree) + geom_nodelab(size=2)

# Read in and process TPA-only vcf (to confirm sites are the same)
TPA.only.midpoint.treetime.ancestral.vcf <- read.vcfR(TPA.treetime.ancestral.vcf.file, verbose = FALSE)
TPA.only.midpoint.treetime.ancestral.vcf.fix <- getFIX(TPA.only.midpoint.treetime.ancestral.vcf)
TPA.only.midpoint.treetime.ancestral.vcf.fix <- data.frame(TPA.only.midpoint.treetime.ancestral.vcf.fix[,c(2,4,5)], stringsAsFactors = F)
TPA.only.midpoint.treetime.ancestral.vcf.fix$in.TPA.only <- "yes"
TPA.only.midpoint.treetime.ancestral.vcf.fix$Key <- 1:nrow(TPA.only.midpoint.treetime.ancestral.vcf.fix)
Extract genotype sites
TPA.treetime.ancestral.vcf.gt <- extract_gt_tidy(TPA.only.midpoint.treetime.ancestral.vcf)
Extracting gt element GT
TPA.treetime.ancestral.vcf.gt.f <- plyr::join(TPA.treetime.ancestral.vcf.gt, TPA.only.midpoint.treetime.ancestral.vcf.fix[,c("Key","POS")], by="Key", type="left")
TPA.treetime.ancestral.vcf.gt.f$POS <- as.numeric(TPA.treetime.ancestral.vcf.gt.f$POS)
TPA.treetime.ancestral.vcf.gt.f$gt_GT <- as.numeric(TPA.treetime.ancestral.vcf.gt.f$gt_GT)
TPA.treetime.ancestral.vcf.gt.f.spread <- tidyr::spread(TPA.treetime.ancestral.vcf.gt.f[,c("POS","Indiv","gt_GT")], POS, gt_GT)
Use snpEff to annotate multi-vcf, and then pull in annotations here
TPA.snpEff <- read.table(TPA.snpEff.file,header = T, check.names = F, comment.char = "",sep="\t")
TPA.snpEff.filt <- TPA.snpEff[!(TPA.snpEff$`ANN[*].GENE`=="gene-TPASS_RS00040" & TPA.snpEff$`ANN[*].EFFECT`=="intragenic_variant"),]
TPA.snpEff.filt[TPA.snpEff.filt$`ANN[*].EFFECT`==".","ANN[*].EFFECT"] <- "intragenic_variant"
TPA.snpEff.filt %>% dplyr::group_by(`ANN[*].EFFECT`) %>% summarise(Count=n())
TPA.snpEff.filt %>% dplyr::group_by(`ANN[*].GENE`) %>% summarise(Count=n())
TPA.snpEff.filt %>% dplyr::group_by(`ANN[*].GENE`,`ANN[*].EFFECT`) %>% summarise(Count=n())
`summarise()` has grouped output by 'ANN[*].GENE'. You can override using the `.groups` argument.
TPA.snpEff.filt.var.per.pos <- TPA.snpEff.filt %>% dplyr::group_by(POS) %>% summarise(Count=n())
TPA.snpEff.filt.var.per.pos.multi <- as.numeric(as.character(unlist(TPA.snpEff.filt.var.per.pos[TPA.snpEff.filt.var.per.pos$Count>1,"POS"])))
TPA.snpEff.filt[TPA.snpEff.filt$POS %in% TPA.snpEff.filt.var.per.pos.multi,]
NA
Lets pull in gene function (where known) for these sites from the gff
SS14.gff <- ape::read.gff(SS14.gff.file)
SS14.gff.cds <- SS14.gff[SS14.gff$type=="CDS",]
#### function to extract different fields from attributes column
getAttributeField <- function (x, field, attrsep = ";") {
s = strsplit(x, split = attrsep, fixed = TRUE)
sapply(s, function(atts) {
a = strsplit(atts, split = "=", fixed = TRUE)
m = match(field, sapply(a, "[", 1))
if (!is.na(m)) {
rv = a[[m]][2]
}
else {
rv = as.character(NA)
}
return(rv)
})
}
###
#getAttributeField(SS14.gff.cds$attributes, "Name")
# Extract attribute elements from gff
SS14.gff.cds$geneid <- gsub("gene\\-","",getAttributeField(SS14.gff.cds$attributes, "Parent"))
SS14.gff.cds$locus_tag <- getAttributeField(SS14.gff.cds$attributes, "locus_tag")
SS14.gff.cds$gene <- getAttributeField(SS14.gff.cds$attributes, "gene")
SS14.gff.cds$product <- getAttributeField(SS14.gff.cds$attributes, "product")
SS14.gff.cds$proteinid <- getAttributeField(SS14.gff.cds$attributes, "protein_id")
# create a merged locus_tag/gene the way snpEff does
SS14.gff.cds$geneid <- sapply(1:nrow(SS14.gff.cds), function(x) ifelse(is.na(SS14.gff.cds$gene[x]),SS14.gff.cds$locus_tag[x], SS14.gff.cds$gene[x]))
SS14.gff.cds$gene.coords <- paste0(SS14.gff.cds$start,":",SS14.gff.cds$end)
SS14.gff.cds
# read in snp classifications, and apply to discriminatory SNPs
Write this as a function. Takes 4 arguments: - dataframe of snps for each sample in wide matrix format (e.g. TPA.treetime.ancestral.vcf.gt.f.spread) - longform list of SNPs and possible alleles (e.g. TPA.treetime.ancestral.vcf.fix) - variant annotations dataframe (e.g. TPA.snpEff.filt) - a vector of two nodes in the tree to compare (e.g. tt.nodes.to.compare.SS14)
extract_branch_site_allelic_functions <- function(allele.matrix.spread, snp.table, snp.annotation.table, nodes.list){
# filter SNP matrix to only include the two nodes of interest
discriminatory.sites1 <- allele.matrix.spread[allele.matrix.spread$Indiv %in% nodes.list,]
discriminatory.sites2 <- tidyr::gather(discriminatory.sites1,POS,Gt,-Indiv) %>%
tidyr::spread(Indiv, Gt)
# Filter SNPs under consideration to those that are different between the two nodes
discriminatory.sites2 <- discriminatory.sites2[(discriminatory.sites2[,2]!=discriminatory.sites2[,3]),]
discriminatory.sites2 <- discriminatory.sites2[order(as.numeric(discriminatory.sites2$POS)),]
# merge in the details about alleles at each relevant SNP position
discriminatory.sites2 <- plyr::join(discriminatory.sites2, snp.table,by=c('POS'), type='left')
# deal with multi-allelic sites, and discriminate between them
discriminatory.sites2$ALT.multi <- discriminatory.sites2$ALT
discriminatory.sites2$ALT <- sapply(1:nrow(discriminatory.sites2), function(x) strsplit(discriminatory.sites2$ALT.multi[x],",")[[1]][sort(as.numeric(((discriminatory.sites2[x,c(2,3)]))))[2]])
# merge in the annotation for the appropriate allele/SNPs
discriminatory.sites2.snpeff <- plyr::join(snp.annotation.table[,c("POS","ALT","ANN[*].ALLELE","ANN[*].EFFECT","ANN[*].GENE","ANN[*].HGVS_C","ANN[*].HGVS_P")], discriminatory.sites2[,c("POS","REF","ALT",nodes.list)], type="right", by=c("POS","ALT"))
discriminatory.sites2.snpeff[is.na(discriminatory.sites2.snpeff$`ANN[*].EFFECT`),"ANN[*].EFFECT"] <- "intragenic_variant"
# return output
return(discriminatory.sites2.snpeff)
}
#tt.nodes.to.compare.SS14.vs.Nichols.TPA <- c("NODE_0000005","NODE_0000103")
#tt.nodes.to.compare.sublineage6.vs.MRCA.TPA <- c("NODE_0000003","NODE_0000002")
tt.nodes.to.compare.sublineage6.vs.MRCA.TPA <- c("NODE_0000001","NODE_0000002")
sublin6.vs.mrca.Nichols.branch_site_alleles.TPA <- extract_branch_site_allelic_functions(TPA.treetime.ancestral.vcf.gt.f.spread,TPA.only.midpoint.treetime.ancestral.vcf.fix,TPA.snpEff.filt, tt.nodes.to.compare.sublineage6.vs.MRCA.TPA)
sublin6.vs.mrca.Nichols.branch_site_alleles.TPA %>% dplyr::group_by(`ANN[*].EFFECT`) %>% dplyr::summarise(count=n())
paste0("All Variants: ", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA))
[1] "All Variants: 51"
paste0("Unique Sites: ", length(unique(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$POS)))
[1] "Unique Sites: 51"
paste0("Synonymous Variants: ", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA[sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$`ANN[*].EFFECT`=="synonymous_variant",]))
[1] "Synonymous Variants: 11"
paste0("Non-Synonymous Variants: ", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA[sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$`ANN[*].EFFECT`=="missense_variant",]))
[1] "Non-Synonymous Variants: 36"
paste0("Intragenic Variants :", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA[sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$`ANN[*].EFFECT`=="intragenic_variant",]))
[1] "Intragenic Variants :4"
sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var <- c(0, sapply(2:nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA) , function(x) as.numeric(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$POS[x]) - as.numeric(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$POS[x-1])))
mean(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 22075.57
median(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 15611
min(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 0
max(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 106334
p.sublineage6.ancestral.SNPs.genomepos <- ggplot(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA, aes(x=as.numeric(POS), y=dist.from.last.var)) +
geom_point(size=1, alpha=0.5) +
#geom_bar(stat='identity', alpha=0.5) +
#geom_line(alpha=0.1) +
theme_light() + theme(text = element_text(size = 10)) +
coord_cartesian(xlim=c(0,SS14.aln.length)) +
scale_x_continuous(breaks=pretty) +
scale_y_log10() +
labs(x="SS14 Genome Position (NC_021508.1; (bp))", y="Distance of variant from previous variant site (bp)", title="Genome position of SNPs delineating Sublineage 6 from MRCA node")
p.sublineage6.ancestral.SNPs.genomepos
Warning: Transformation introduced infinite values in continuous y-axis

p.sublineage6.ancestral.SNPs.dist.between.histo <- sublin6.vs.mrca.Nichols.branch_site_alleles.TPA %>%
ggplot(aes(x=dist.from.last.var)) +
scale_x_log10() +
geom_histogram(bins=50) +
theme_light() + theme(text = element_text(size = 10)) +
labs(x="Distance of variant from previous variant site (bp)", y="Count") + coord_flip()
p.sublineage6.ancestral.SNPs.dist.between.histo
Warning: Transformation introduced infinite values in continuous x-axis
Warning: Removed 1 rows containing non-finite values (stat_bin).

plot_grid(p.sublineage6.ancestral.SNPs.genomepos, p.sublineage6.ancestral.SNPs.dist.between.histo + y.theme.strip , rel_widths = c(8,1), align = T)
Warning: Transformation introduced infinite values in continuous y-axis
Warning: Transformation introduced infinite values in continuous x-axis
Warning: Removed 1 rows containing non-finite values (stat_bin).

Do some further analysis of the North East sublineage distributions. We have 35 samples collected from these regions, of which 17 were collected from 2014 onwards. Is sublineage 14 missing by chance (could we be missing it simply because we haven’t collected enough samples) or is this more likely to reflect true uneven regional distributions?
# How many genomes found in Northern regions before and after first detection of sublineage 14 in 2014?
PHE.metadata.linked %>%
dplyr::mutate(before2014=ifelse(year>=2014,"2014onwards", "pre2014")) %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(before2014) %>%
dplyr::summarise(count=n())
# What are the proportions of different sublineages around the UK before and after 2014?
PHE.meta.post2014.sublin.fracs <- PHE.metadata.linked %>%
#dplyr::filter(year>=2014) %>%
dplyr::mutate(before2014=ifelse(year>=2014,"2014onwards", "pre2014")) %>%
dplyr::group_by(before2014, TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.all=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.all) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))
`summarise()` has grouped output by 'before2014'. You can override using the `.groups` argument.
PHE.meta.post2014.sublin.fracs
# simulating poisson process r to work out how many samples we would expect in Northern England under poisson distribution
# What % of sublineage 14 samples are found in the total population?
post2014.sublin14.freq <- PHE.meta.post2014.sublin.fracs %>% filter(before2014=="2014onwards", TPA.pinecone.sublineage==14) %>% select(Lineage.perc) %>% pull()
Adding missing grouping variables: `before2014`
# Simulate and plot a Poisson distribution of how many sublineage 14 samples we would expect to find if we randomly selected 17 samples at 22%
data.frame(rpois=rpois(1000000, 17/(100/post2014.sublin14.freq))) %>%
ggplot(aes(rpois)) + geom_histogram(binwidth=1) +
scale_x_continuous(breaks=seq(0,20,2)) +
theme_light() +
labs(x="Samples Found", y="Simulation Count")

# What are the quantile distributions from that?
quantile(rpois(1000000, 17/(100/post2014.sublin14.freq)), probs=c(0.01, 0.05, 0.5, 0.95, 0.99))
1% 5% 50% 95% 99%
0 1 4 7 9
median(rpois(1000000, 17/(100/post2014.sublin14.freq)))
[1] 4
mean(rpois(1000000, 17/(100/post2014.sublin14.freq)))
[1] 3.798339
# What is the probability of finding no samples (assuming uniform unbiased coverage)?
data.frame(n=seq(0,20,1), dpois=sapply(seq(0,20,1), function(x) dpois(x, lambda=17/(100/post2014.sublin14.freq)))) %>%
ggplot(aes(x=n, y=dpois)) +
geom_bar(stat='identity') +
scale_x_continuous(breaks=pretty) +
theme_light() +
labs(x="Samples Found", y="Probability")

paste("Probability of finding zero samples is ", round(dpois(0, lambda=17/(100/post2014.sublin14.freq)), 5))
[1] "Probability of finding zero samples is 0.02244"
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFRyZXBvbmVtYSBVSyBVS0hTQS1jb2hvcnQgQW5hbHlzaXMgMjAyMi4gUmV2aXNpb24gMDQtMjAyMyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKTWFrZSBhIGNsZWFuIGVudmlyb25tZW50CmBgYHtyfQogcm0obGlzdD1scygpKQpgYGAKXApMb2FkIHBhY2thZ2VzCmBgYHtyfQpwYWNrYWdlcy5saXN0IDwtIGMoImdncGxvdDIiLCJ0cmVlaW8iLCJnZ3RyZWUiLCJnZ25ld3NjYWxlIiwiYXBlIiwiZHBseXIiLCJ0aWR5dmVyc2UiLCJ0aWR5ciIsInBoeXRvb2xzIiwiUkNvbG9yQnJld2VyIiwibHVicmlkYXRlIiwicmVhZHhsIiwiZ2dmb3JjZSIsImdnc3RhbmNlIiwiZ2dyaWRnZXMiLCJjb3dwbG90IiwiaGV4YmluIiwic2NhbGVzIiwiaGF2ZW4iLCJuZXR3b3JrIiwiZ2duZXR3b3JrIiwiaW50ZXJncmFwaCIsImlncmFwaCIsImdncmFwaCIsImdyYXBobGF5b3V0cyIsInNjYXR0ZXJwaWUiLCJtYXBzIiwibWFwZGF0YSIsIm1hcHRvb2xzIiwicmdkYWwiLCJyZ2VvcyIsImJyb29tIiwiZ2dyZXBlbCIsImdncmlkZ2VzIiwibWFnaWNrIiwiZ2diZWVzd2FybSIsImdncmFzdHIiKQoKIyJwbHlyIiwiQ2Fpcm8iLCJnZ21hcCIsImVtb2ppZm9udCIsInJQaW5lY29uZSIsInBhaXJzbnAiLCJDb29yZGluYXRlQ2xlYW5lciIsImdyaWRFeHRyYSIsImRlbmRleHRlbmQiLCJnZ2RlbmRybyIsCgojQmlvY01hbmFnZXI6Omluc3RhbGwoImdndHJlZSIpCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgidHJlZWlvIikKCmZvcihwa2cgaW4gcGFja2FnZXMubGlzdCl7CiAgZXZhbChicXVvdGUobGlicmFyeSguKHBrZykpKSkgfQpgYGAKXApDb25maXJtIGN1cnJlbnQgZW52aXJvbm1lbnRhbCBzZXR1cApgYGB7cn0KUi5WZXJzaW9uKCkKcHJpbnQoc2Vzc2lvbkluZm8oKSkKYGBgClwKTWFrZSBzb21lIHNob3J0Y3V0cyBmb3IgcGxvdHRpbmcgCmBgYHtyfQp5LnRoZW1lLnN0cmlwIDwtIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueT0gZWxlbWVudF9ibGFuaygpKQp5LnRoZW1lLnN0cmlwLnBhcnRpYWwgPC0gdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueT0gZWxlbWVudF9ibGFuaygpKQoKeC50aGVtZS5zdHJpcCA8LSB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9IGVsZW1lbnRfYmxhbmsoKSkKeC50aGVtZS5zdHJpcC5wYXJ0aWFsIDwtIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9IGVsZW1lbnRfYmxhbmsoKSkKeC50aGVtZS5zdHJpcC5sYWJzIDwtIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkKCngudGhlbWUuYXhpcy5yb3RhdGUgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQoKbGVnZW5kLnN0cmlwIDwtIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnRoZW1lLnRleHQuc2l6ZSA8LSB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpCgonJW5vdGluJScgPC0gTmVnYXRlKCclaW4lJykKCm1heC5mb250LnNpemUgPC0gNwpiYXNpYy5mb250LnNpemUgPC0gNgptaW4uZm9udC5zaXplIDwtIDUuMjUKdGhlbWUudGV4dC5zaXplIDwtIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGJhc2ljLmZvbnQuc2l6ZSkpCnRoZW1lLnRleHQuc2l6ZS53aXRoaW4gPC0gKDUvMTQpKm1pbi5mb250LnNpemUKcGFuZWwubGFiLnNpemUgPC0gMTAKCmBgYApcClNwZWNpZnkgcmF3IGRhdGEgLSBnbG9iYWwgZGF0YXNldApgYGB7cn0KI0RhdGFfaW5wdXRfZGlyZWN0b3J5IDwtICIvVXNlcnMvbWIyOS9QYXBlcnMvVHJlcG9uZW1hX1VLLVBIRS1nZW4tZXBpXzIwMjEvRGF0YS8iCiNEYXRhX2lucHV0X2RpcmVjdG9yeSA8LSAiL1VzZXJzL21iMjkvUGFwZXJzL1RyZXBvbmVtYV9VSy1QSEUtZ2VuLWVwaV8yMDIxL1Jub3RlYm9vay9Sbm90ZWJvb2tfMDktMjAyMi9kYXRhLyIKRGF0YV9pbnB1dF9kaXJlY3RvcnkgPC0gcGFzdGUwKGdldHdkKCksICIvaW5wdXRkYXRhLyIpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyBUcmVlIGRhdGEgCgojIE1MIHRyZWUgKHJlZmluZWQgZGF0YXNldCkKVFBBLk1MdHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLXViZXIucmVtYXNrZWQuMjAyMC0xMS0xMC5nb29kY292MjUuZ3ViYmlucy5TTlBzLmFsbi5yZW5hbWVkLmZpeC16ZXJvLWRpc3QudHJlZWZpbGUiKQoKIyBQeWphciB0cmVlIChyZWZpbmVkIGRhdGFzZXQpClRQQS5weWphci5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLXViZXIucmVtYXNrZWQuMjAyMC0xMS0xMC5nb29kY292MjUuZ3ViYmlucy5TTlBzLmFsbi5yZW5hbWVkLnB5amFyLnRyZSIpCgojIEZ1bGwgc2l6ZSBCRUFTVDIgYW5hbHlzaXMgLSBwcmV2aW91c2x5IGdlbmVyYXRlZCBhcyBwYXJ0IG9mIEJlYWxlLCAyMDIxLgpmdWxsLmJlYXN0Mi50cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlcl9iZWFzdDJfc3RyaWN0LXNreWxpbmUtNTAwTV8xMHBvcF9jb25zZW5zdXMudHJlZSIpCgojIEFuY2VzdHJhbCByZWNvbnN0cnVjdGlvbiBvZiBnbG9iYWwgVFBBIE1MIHRyZWUgZnJvbSBUcmVlVGltZSAocmVmaW5lZCBkYXRhc2V0KQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5hbm5vdGF0ZWRfdHJlZS5maXgtaHVuZy5uZXh1cyIpClRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5taWRwb2ludC5hbmNlc3RyYWxfc2VxdWVuY2VzLmZpeC1odW5nLnZjZiIpCiMgRnVuY3Rpb25hbGx5IGFubm90YXRlZCB2YXJpYW50cywgZXh0cmFjdGVkIGZyb20gc25wRWZmIHZjZiBpbnRvIHRzdiB1c2luZyBzbnBTaWZ0ClRQQS5zbnBFZmYuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyLm1pZHBvaW50LmFuY2VzdHJhbF9zZXF1ZW5jZXMucmVsYWIuYmNmLmFubi52Y2YudmFydGFiLnNlcGxpbmUudHN2IikKIyBHZmYgZmlsZSBmb3IgU1MxNCByZWZlcmVuY2UgZ2Vub21lLCBjb250YWluaW5nIGdlbmUgcG9zaXRpb25zL2Fubm90YXRpb25zClNTMTQuZ2ZmLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUcmVwb25lbWFfcGFsbGlkdW1fc3Vicy5fcGFsbGlkdW1fU1MxNC5OQ18wMjE1MDguMS4yMDIxLTA2LTEzLmdmZiIpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIE1ldGEgZGF0YSAKCiMgU3VwcGxlbWVudCBmcm9tIFRQQS1VYmVyIHBhcGVyIC0gQmVhbGUsIDIwMjEgClRQQS5tZXRhMi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiU3VwX0RhdGExX0dsb2JhbF9TYW1wbGUtTWV0YWRhdGFfXzA5LTIwMjIueGxzeCIpCgojIEVuZ2xhbmQgc3BlY2lmaWMgbWV0YWRhdGEgY29sbGF0ZWQgYnkgUEhFL1VLSFNBClBIRS5tZXRhZGF0YS5saW5rZWQuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlN1cF9EYXRhMl9UUEEuVUstb25seS5QSEUubWV0YWRhdGEuMjAyMi0wMi0wMi54bHN4IikKCiMgRW5nbGFuZCBzcGVjaWZpYyBtYXBwaW5nIHNoYXBlZmlsZSBkYXRhIHdpdGggUHVibGljIEhlYWx0aCBCb3VuZGFyaWVzCiMgSW1wb3J0ZWQgZGF0YWZpbGUgZnJvbSBodHRwczovL2dlb3BvcnRhbC5zdGF0aXN0aWNzLmdvdi51ay9kYXRhc2V0cy9wdWJsaWMtaGVhbHRoLWVuZ2xhbmQtY2VudHJlcy1kZWNlbWJlci0yMDE2LWZ1bGwtY2xpcHBlZC1ib3VuZGFyaWVzLWluLWVuZ2xhbmQvZXhwbG9yZT9sb2NhdGlvbj01Mi45NTAwMDAlMkMtMi4wMDAwMDAlMkM2Ljg4ClVLLnB1YmxpY2hlYWx0aC5zaGFwZWZpbGUuZGF0YSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlB1YmxpY19IZWFsdGhfRW5nbGFuZF9DZW50cmVzXyhEZWNlbWJlcl8yMDE2KV9Cb3VuZGFyaWVzIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIEV4dGVybmFsbHkgcGxvdHRlZCBmaWd1cmVzIChlLmcuIEdyYXBlVHJlZSkgZm9yIGluY2x1c2lvbiBpbiBtdWx0aXBhbmVsIGZpZ3VyZXMKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIHdob2xlIG9mIEVuZ2xhbmQgLSBjb2RlIHRvIGV4dHJhY3Qgc3VidHJlZSB0aGF0IHdhcyB1c2VkIHRvIG1ha2UgdGhpcyBpcyBpbmNsdWRlZCBsYXRlciBpbiB0aGlzIFJub3RlYm9vawpUUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstMjAyMi0wMi0wMy5zdWJsaW5lYWdlLU1TVHJlZS5JbmtzY2FwZWQuc3ZnIikKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIHdob2xlIG9mIEVuZ2xhbmQgLSAzLXZhcmlhYmxlIHBsb3RzClRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMy5zdmciKQoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIEhJViBzdGF0dXMKVFBBLlVLLkdyYXBldHJlZS5ISVYuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy0yMDIyLTAyLTAzLkhJVnN0YXR1cy1NU1RyZWVfaW5rc2NhcGVkLnN2ZyIpCgojIEV4dGVybmFsbHkgcGxvdHRlZCBncmFwZXRyZWUgbWluaW11bSBzcGFubmluZyB0cmVlIGZvciBOb3J0aCBFYXN0IEVuZ2xhbmQgbmV0d29ya3MKVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstTm9ydGhFYXN0LTIwMjItMDItMjYuR2VuZGVyT3JpZW50YXRpb24tTVNUcmVlLmlua3NjYXBlZC4rbm9kZS1jb3VudHMrR0JNU00uc3ZnIikKCgpgYGAKXApTcGVjaWZ5IGRpcmVjdG9yeSB0byBvdXRwdXQgcGxvdHMKYGBge3J9CkZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5IDwtIHBhc3RlMChnZXR3ZCgpLCAiL0ZpZ3VyZXNfcmV2aXNpb25fMDMtMjAyMy8iKQoKIyIvVXNlcnMvbWIyOS9QYXBlcnMvVHJlcG9uZW1hX1VLLVBIRS1nZW4tZXBpXzIwMjEvRmlndXJlcy9GaWd1cmVfRHJhZnRpbmcvV29ya2luZ19GaWd1cmVzXzA4LTIwMjIvIgpgYGAKXApSZWFkIGluIHRyZWVzCmBgYHtyfQpUUEEuTUx0cmVlIDwtIG1pZHBvaW50LnJvb3QocmVhZC50cmVlKFRQQS5NTHRyZWUuZmlsZSkpClRQQS5weWphci50cmVlIDwtIG1pZHBvaW50LnJvb3QocmVhZC50cmVlKFRQQS5weWphci5maWxlKSkKYGBgClwKUmVhZCBpbiBmaW5hbCBvdXRwdXQgbWV0YWRhdGEgZnJvbSBHbG9iYWwgVWJlciBzdHVkeSAoQmVhbGUgMjAyMSkKYGBge3J9ClRQQS5tZXRhMi4xIDwtIHJlYWR4bDo6cmVhZF9leGNlbChUUEEubWV0YTIuZmlsZSxzaGVldD0iU3VwcGxlbWVudGFyeV9EYXRhMV9TYW1wbGUtTWV0YSIpCmBgYApcCkNyZWF0ZSBhIGNvbG91ciBzY2hlbWUgZm9yIExpbmVhZ2VzLCBDb3VudHJpZXMgYW5kIENvbnRpbmVudHMgKGNvbnNpc3RlbnQgd2l0aCBCZWFsZSwgMjAyMSkKYGBge3J9CiMgQ29sb3VyaW5nIGZvciBjb3VudHJ5CmNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiA8LSB1bmlxdWUoVFBBLm1ldGEyLjFbLGMoIkdlb19Db3VudHJ5IiwiQ29udGluZW50IildKQpjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIgPC0gY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyW29yZGVyKGNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiRDb250aW5lbnQsY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJEdlb19Db3VudHJ5KSxdCgpjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkY291bnRyeS5jb2wgPC0gYygiI2VjNzAxNCIsIiNmZWM0NGYiLCIjZGUyZDI2IiwiI2ZiNmE0YSIsIiNiZGJkYmQiLCIjNzM3MzczIixicmV3ZXIucGFsKG49OCwiUHVycGxlcyIpWzQ6OF0sYnJld2VyLnBhbChuPTgsIkJsdWVzIilbMzo4XSxicmV3ZXIucGFsKG49NSwiR3JlZW5zIilbMzo1XSwiI2M1MWI4YSIsIiM4YzUxMGEiKQoKIyBDb2xvdXJpbmcgZm9yIENvbnRpbmVudApjb250aW5lbnRhbC5jb2xzLmJyZXcyIDwtIGRhdGEuZnJhbWUoQ29udGluZW50PXNvcnQodW5pcXVlKFRQQS5tZXRhMi4xJENvbnRpbmVudCkpLHN0cmluZ3NBc0ZhY3RvcnM9RikKY29udGluZW50YWwuY29scy5icmV3MiRjb250aW5lbnQuY29sIDwtIGMoIiNmZWM0NGYiLCIjZGUyZDI2IiwiI2JkYmRiZCIsIiMyMTcxYjUiLCIjNzRjNDc2IiwiI2M1MWI4YSIsIiNlYzcwMTQiKQoKCiMgQ29sb3VyaW5nIGZvciBUUEEgTGluZWFnZQpUUEFfTGluZWFnZS5jb2xzIDwtIGRhdGEuZnJhbWUoTGluZWFnZT1zb3J0KHVuaXF1ZShUUEEubWV0YTIuMSRUUEFfTGluZWFnZSkpLHN0cmluZ3NBc0ZhY3RvcnM9RikKVFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCA8LSBjKCJyb3lhbGJsdWUyIiwgImluZGlhbnJlZDEiKQojYygiIzQzNmVlZSIsICIjNjY2NjY2IiwiI2ZmNmE2YSIpClRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSA8LSBmYWN0b3IoVFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLCBsZXZlbHM9YygiTmljaG9scyIsIlNTMTQiLCJvdXRsaWVyIikpCgojIExpbmVhZ2UgSGV4Y29kZXMKIyByb3lhbGJsdWUyICM0MzZlZWUKIyBpbmRpYW5yZWQxICNmZjZhNmEKYGBgClwKRGVmaW5lIGNvbG91cnMgZm9yIHN1YmxpbmVhZ2VzCmBgYHtyfQojIERlZmluZSBzdWJsaW5lYWdlIGNsdXN0ZXJpbmcgc2NoZW1lIHVzaW5nIGJyZXcgY29sb3Vyc2NhbGVzCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBkYXRhLmZyYW1lKHVuaXF1ZShUUEEubWV0YTIuMVssYygiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gc3VibGluZWFnZXMuY29scy5icmV3W29yZGVyKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEFfTGluZWFnZSxzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLF0KCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW4ub3JkZXIgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1tvcmRlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluLm9yZGVyKSxdCgojIEZvciByZXZpc2VkIGJvb3RzdHJhcHBlZCBjbHVzdGVycwpzdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzIDwtIGMoIiNGQzkyNzIiLCIjRUYzQjJDIixicmV3ZXIucGFsKG49NCwiR3JlZW5zIilbMjo0XSxicmV3ZXIucGFsKG49NCwiWWxPckJyIilbYygyLDMpXSxicmV3ZXIucGFsKG49NiwiQmx1ZXMiKVsyOjZdLGJyZXdlci5wYWwobj02LCJQdXJwbGVzIilbMjo2XSwiZ3JleTgwIiwiZ3JleTgwIiwiZ3JleTgwIiwiZ3JleTgwIikKICAKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHVuaXF1ZShzdWJsaW5lYWdlcy5jb2xzLmJyZXdbLGMoIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwic3VibGluZWFnZS5jb2xzIildKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gc3VibGluZWFnZXMuY29scy5icmV3W29yZGVyKGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSkpKSxdCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSA8LSBmYWN0b3Ioc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBsZXZlbHM9c3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gc3VibGluZWFnZXMuY29scy5icmV3WyFpcy5uYShzdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSksXQoKY29sbmFtZXMoc3VibGluZWFnZXMuY29scy5icmV3KSA8LSBjKCJzdWJsaW5lYWdlIiwic3VibGluZWFnZS5jb2xzIikKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHVuaXF1ZShzdWJsaW5lYWdlcy5jb2xzLmJyZXcpCmBgYApcClJlc3RyaWN0IGFuYWx5c2lzIHRvIGhpZ2ggcXVhbGl0eSBnZW5vbWVzIChhbmQgdHJlZSkKYGBge3J9ClRQQS5tZXRhMi4xIDwtIFRQQS5tZXRhMi4xW1RQQS5tZXRhMi4xJGZpbmVzY2FsZS5hbmFseXNpcz09IlllcyIsXQpgYGAKXApDcmVhdGUgYSAiVUsiIHZhcmlhYmxlLCBhbmQgYSAiUEhFIiB2YXJpYWJsZQpgYGB7cn0KVFBBLm1ldGEyLjEkaXMuVUsgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJEdlb19Db3VudHJ5PT0iVUsiLCJVSyIsIk90aGVyIikKVFBBLm1ldGEyLjEkaXMuUEhFIDwtIGlmZWxzZShUUEEubWV0YTIuMSRHZW9fQ291bnRyeT09IlVLIiAmIGdyZXBsKCJQSEUiLFRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lKSwiUEhFIiwiT3RoZXIiKQpgYGAKXApgYGB7cn0KIyBQcmVwYXJlIE1MIHRyZWUKVFBBLk1MdHJlZS5nZ3RyZWUgPC0gZ2d0cmVlKFRQQS5NTHRyZWUsbGF5b3V0ID0gImZhbiIsb3Blbi5hbmdsZSA9IDEwLCByaWdodD1UKQoKIyBQcmVwYXJlIGNvdW50cnkgZGF0YXNldApUUEEucmF3c2VxLmNvdW50cmllcy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBDb3VudHJ5PVRQQS5tZXRhMi4xJEdlb19Db3VudHJ5LCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgUHJlcGFyZSBjb250aW5lbnQgZGF0YXNldApUUEEucmF3c2VxLmNvbnRpbmVudHMucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgQ29udGluZW50PVRQQS5tZXRhMi4xJENvbnRpbmVudCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgVUsgZGF0YSBzdHJpcApUUEEucmF3c2VxLlVLLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIEVuZ2xhbmQ9VFBBLm1ldGEyLjEkaXMuVUssIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpUUEEucmF3c2VxLlVLLnBbVFBBLnJhd3NlcS5VSy5wJEVuZ2xhbmQ9PSJVSyIsXSA8LSAiRW5nbGFuZCIKCiMgUHJlcGFyZSBQSEUgZGF0YSBzdHJpcApUUEEucmF3c2VxLlBIRS5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBQSEU9VFBBLm1ldGEyLjEkaXMuUEhFLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgUHJlcGFyZSBNYWpvciBsaW5lYWdlIGRhdGFzZXQKVFBBLnJhd3NlcS5MaW5lYWdlLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIExpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBX0xpbmVhZ2UsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIHN1YmxpbmVhZ2UgbGluZWFnZSBkYXRhc2V0ClRQQS5yYXdzZXEuc3ViTGluZWFnZS5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCgojIFByZXBhcmUgWWVhciBkYXRhc2V0IChhbGwgc2FtcGxlcykKVFBBLnJhd3NlcS5hbGwuWWVhcnMucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgWWVhcj1UUEEubWV0YTIuMSRTYW1wbGVfWWVhciwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgoKZmxvb3JfNXllYXJzICA8LSBmdW5jdGlvbih2YWx1ZSl7IHJldHVybih2YWx1ZSAtIHZhbHVlICUlIDUpIH0KVFBBLm1ldGEyLjEkU2FtcGxlXzV5ZWFyLndpbmRvdyA8LSBwYXN0ZTAoZmxvb3JfNXllYXJzKGFzLm51bWVyaWMoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXIpKSwiLSIsZmxvb3JfNXllYXJzKGFzLm51bWVyaWMoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXIpKSs1KQojIFNvbWUgc2FtcGxlcyBoYXZlIHVuY2VydGFpbiBkYXRlcyAodXAgdG8gMjAtMzAgeWVhcnMgdW5jZXJ0YWludHkpLCBidXQgZm9yIHRoZSBwdXJwb3NlcyBvZiB0aGVzZSBwbG90dGluZyBjYXRlZ29yaWVzIHdlJ2xsIHVzZSB0aGUgY2VudHJlcG9pbnQgeWVhcgpUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93IDwtIHNhcHBseSgxOm5yb3coVFBBLm1ldGEyLjEpLCBmdW5jdGlvbih4KSBpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXJbeF09PSItIixOQSwgaWZlbHNlKGlzLm5hKFRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3dbeF0pLE5BLCBpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXJbeF09PSIxOTUwLTE5ODAiLCIxOTY1LTE5NzAiLGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhclt4XT09IjE5NjAtMTk4MCIsIjE5NjUtMTk3MCIgLGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhclt4XT09IjE5ODAtMTk5OSIsIjE5ODUtMTk5MCIsVFBBLm1ldGEyLjEkU2FtcGxlXzV5ZWFyLndpbmRvd1t4XSkpKSkpKQoKClRQQS5tZXRhMi4xJFNhbXBsZV95ZWFyLjE5OTAuY3V0dG9mZiA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXI+MTk5MCxUUEEubWV0YTIuMSRTYW1wbGVfWWVhciwiPDE5OTAiKQoKVFBBLm1ldGEyLjEkU2FtcGxlX3llYXIuMTk5OS5jdXR0b2ZmIDwtIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhcj4xOTk5LFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyLCI8MTk5OSIpClRQQS5yYXdzZXEueWVhci5jdXR0b2ZmLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFNhbXBsZS5ZZWFyPVRQQS5tZXRhMi4xJFNhbXBsZV95ZWFyLjE5OTkuY3V0dG9mZiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgpgYGAKXApcCiMgQnJpbmcgaW4gUEhFIG1ldGFkYXRhCmBgYHtyfQpQSEUubWV0YWRhdGEubGlua2VkIDwtIHJlYWR4bDo6cmVhZF9leGNlbChQSEUubWV0YWRhdGEubGlua2VkLmZpbGUpCmBgYApcCkRvIHNvbWUgY2xlYW51cCBhbmQgZmFjdG9yaW5nIG9mIHZhcmlhYmxlcwpgYGB7cn0KClBIRS5tZXRhZGF0YS5saW5rZWQkYWdlX2dyb3VwIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJGFnZV9ncm91cCwgbGV2ZWxzPXJldihjKCIxNi0yNCIsIjI1LTM0IiwiMzUtNDQiLCI0NSsiLCJVbmtub3duIikpKQoKUEhFLm1ldGFkYXRhLmxpbmtlZCRsb25kb24gPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkbG9uZG9uLGxldmVscz1yZXYoYygiWWVzIiwiTm8iLCJVbmtub3duIikpKQpQSEUubWV0YWRhdGEubGlua2VkJHVrYm9ybiA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCR1a2Jvcm4sbGV2ZWxzPXJldihjKCJZZXMiLCJObyIsIlVua25vd24iKSkpClBIRS5tZXRhZGF0YS5saW5rZWQkaGl2cG9zIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJGhpdnBvcywgbGV2ZWxzPXJldihjKCJZZXMiLCJObyIsIlVua25vd24iKSkpCgojIG5lZWQgdG8gdXBkYXRlIHRlcm1pbm9sb2d5IG9mICdNU00nIHRvICdHQk1TTScKUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbj09Ik1TTSIsImdlbmRlcl9vcmllbnRhdGlvbiJdIDwtICJHQk1TTSIKUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb24gPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uLCBsZXZlbHM9cmV2KGMoIk1TVyIsIkdCTVNNIiwiV1NNIiwiTVVua25vd24iLCJVbmtub3duIikpKQoKUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUsIGxldmVscz1yZXYoYygiRWFzdCBNaWRsYW5kcyIsICJFYXN0IG9mIEVuZ2xhbmQiLCAiTG9uZG9uIiwgIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJTb3V0aCBFYXN0IiwgIlNvdXRoIFdlc3QiLCAiV2VzdCBNaWRsYW5kcyIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIsICJVSyAobm90IEVuZ2xhbmQpIiwgIk5vdCBLbm93biIpKSkKClBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBsZXZlbHM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCgpgYGAKXApcCiMjIyBFeHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IGR1cGxpY2F0ZXMKYGBge3J9ClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbIWlzLm5hKFBIRS5tZXRhZGF0YS5saW5rZWQkZHVwX2ZsYWcpLF0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMgPC0gUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXNbIWlzLm5hKFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzJFNhbXBsZV9OYW1lKSxdCgoKUEhFLnBhdGllbnQubWF0Y2hlcyA8LSBkYXRhLmZyYW1lKAogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR1cF9mbGFnID0gYygiMUEiLCIxQiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyQSIsIjJCIiwiM0EiLCIzQiIsIjRBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjRCIiwiNUEiLCI1QiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR1cF9QYXRpZW50ID0gYygiUGF0aWVudCAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGllbnQgMSIsIlBhdGllbnQgMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDIiLCJQYXRpZW50IDMiLCJQYXRpZW50IDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCA0IiwiUGF0aWVudCA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGllbnQgNSIsIlBhdGllbnQgNSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZHVwX1BhdGllbnRfU2FtcGxlID0gYygic2FtcGxlIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlIDIiLCJzYW1wbGUgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMiIsInNhbXBsZSAxIiwic2FtcGxlIDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlIDEiLCJzYW1wbGUgMiIsInNhbXBsZSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAyIikKICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgCgpQSEUubWV0YWRhdGEuZHVwbGljYXRlcyA8LSBsZWZ0X2pvaW4oUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMsIFBIRS5wYXRpZW50Lm1hdGNoZXMsIGJ5PSJkdXBfZmxhZyIpCgpQSEUubWV0YWRhdGEuZHVwbGljYXRlcwpgYGAKCkR1cGxpY2F0ZSBTYW1wbGVzIG1pc3NpbmcgbWV0YWRhdGEgYXJlIGFsbCAnbmV3IGR1cGxpY2F0ZXMnIGFuZCB3ZXJlIGV4Y2x1ZGVkIGR1ZSB0byBsb3cgbWFwcGluZyBjb3ZlcmFnZSAoYWxsIGNoZWNrZWQpLgpcClNhbXBsZXMgbGFiZWxsZWQgJ1pBJyBhbmQgJ1hCJyBoYWQgZHVwbGljYXRlcyBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldCwgYnV0IHRoZSByZWNpcHJvY2FsIHBhaXJzIHdlcmUgZXhjbHVkZWQgZHVlIHRvIHF1YWxpdHkgaXN1ZXMuClwKQXZhaWxhYmxlIHBhaXJzIC0gUGF0aWVudCAzLCBQYXRpZW50IDQKCmBgYHtyfQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWQgPC0gUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXNbUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMkZHVwX1BhdGllbnQgJWluJSBjKCJQYXRpZW50IDMiLCJQYXRpZW50IDQiKSxdClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZFtvcmRlcihQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWQkZHVwX1BhdGllbnQsIFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZCR5ZWFyLFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZCRtb250aCksYygiU2FtcGxlX05hbWUiLCJkdXBfUGF0aWVudCIsICJtb250aC5maXgiLCAieWVhciIpXQpgYGAKXApUaGVzZSB3aWxsIGJlIHJldmlzaXRlZCBsYXRlciBpbiB0aGUgYW5hbHlzaXMuIApcClBhdGllbnQgNApISVYtdmUgTVNNICg0NSspLCBVSyBib3JuLCBQSEUgcmVnaW9uIEQKMiBzYW1wbGVzLCBjb2xsZWN0ZWQgaW4gdGhlIHNhbWUgbW9udGggYW5kIHllYXIKQm90aCBzYW1wbGVzIGFyZSBzdWJsaW5lYWdlIDEsIGFuZCBpZGVudGljYWwgKDAgcHdTTlBzKQpMaWtlbHkgdGhlIHNhbWUgaW5mZWN0aW9uIChkZXBlbmRpbmcgb24gZGF0ZXMsIHRyZWF0bWVudCwgZXRjKSwgYnV0IGNhbuKAmXQgcnVsZSBvdXQgcmVpbmZlY3Rpb24gd2l0aCBzYW1lIHN0cmFpbi4KXApQYXRpZW50IDMKSElWLXZlIE1TTSAoMzUtNDQpLCBub3QgVUsgYm9ybiwgYmFzZWQgaW4gTG9uZG9uIChDKQoyIHNhbXBsZXMsIGNvbGxlY3RlZCA5IG1vbnRocyBhcGFydApCb3RoIHNhbXBsZXMgYXJlIHN1YmxpbmVhZ2UgMSwgYnV0IGhhdmUgNyBwYWlyd2lzZSBTTlBzIGJldHdlZW4gdGhlbSAobG9hZHMhKQpSZWluZmVjdGlvbiDigJMgcHJvYmFibHkgZnJvbSBhIGRpZmZlcmVudCB0cmFuc21pc3Npb24gbmV0d29yawpcClwKSG93ZXZlciwgYmFzZWQgb24gdGhlIHNhbXBsZSBkYXRlcywgYXMgd2VsbCBhcyB0aGUgb3V0Y29tZSBvZiB0aGUgZG93bnN0cmVhbSBnZW5ldGljIGFuYWx5c2lzLCB3ZSBjYW4gc2VlIHRoYXQgUGF0aWVudCAzIGhhcyBkdXBsaWNhdGUgaW5mZWN0aW9uIGV2ZW50cyAoZGlmZmVyZW50IGRhdGVzLCAxMCBtb250aHMgYXBhcnQpIGFuZCB0aGUgZ2Vub21lcyBhcmUgZGlzdGluY3QgKDcgU05QcyBhcGFydCksIHdoZXJlYXMgUGF0aWVudCA0IHNhbXBsZXMgd2VyZSBjb2xsZWN0ZWQgaW4gdGhlIHNhbWUgbW9udGggYW5kIHllYXIgKGkuZS4gYXJlIGxpa2VseSBkdXBsaWNhdGVzIGZyb20gdGhlIHNhbWUgaW5mZWN0aW9uKSBhbmQgaGFzIGlkZW50aWNhbCBnZW5vbWVzLgpcCkZvciBkb3duc3RyZWFtIGFuYWx5c2lzIHB1cnBvc2VzLCB3ZSB3aWxsIHJldGFpbiBib3RoIHNhbXBsZXMgZm9yIFBhdGllbnQgMyAoZGlzY3JldGUgaW5mZWN0aW9ucyksIGJ1dCBleGNsdWRlIG9uZSBzYW1wbGUgZnJvbSBQYXRpZW50IDQgKGR1cGxpY2F0ZSBpbmZlY3Rpb24gc2FtcGxlcykgLSAnUEhFMTUwMTI2QScgaGFzIG11Y2ggYmV0dGVyIGdlbm9tZSBjb3ZlcmFnZSwgc28gZXhjbHVkZSAnUEhFMTUwMTI1QScKXApcCiMjIyBGdXJ0aGVyIEV4Y2x1c2lvbnMgXApQSEUxMzAwNTZBIC0gZHVwbGljYXRlIG9mIFBIRTEzMDA1N0IgKGFscmVhZHkgcmVtb3ZlZCwgc28gbm90IHJlbGV2YW50KSAtIGRvbid0IGV4Y2x1ZGUhClBIRTE3MDQwMkEgLSBxdWFsaXR5IGNvbnRyb2wgc2FtcGxlClBIRTE3MDM3OEEgLSBxdWFsaXR5IGNvbnRyb2wgc2FtcGxlCgpcCkV4Y2x1ZGUgZHVwbGljYXRlIHNlcXVlbmNlcwpgYGB7cn0KZHVwbGljYXRlLmV4Y2x1c2lvbi5saXN0IDwtIGMoIlBIRTE1MDEyNUEiLCJQSEUxNzA0MDJBIiwiUEhFMTcwMzc4QSIpClBIRS5tZXRhZGF0YS5saW5rZWQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lICVub3RpbiUgZHVwbGljYXRlLmV4Y2x1c2lvbi5saXN0LF0KYGBgClwKCiMjIyBNb3Zpbmcgb24uLi4gXAoKRGVmaW5lIHNvbWUgY29sb3VyIHNjaGVtZXMKYGBge3J9CiMgZGVmaW5lIHNvbWUgY29sb3JzIGZvciBlYWNoIHJlZ2lvbgpQSEUucmVnaW9uLmNvbHMuYnJldyA8LSBkYXRhLmZyYW1lKFVLSFNBLnJlZ2lvbj1jKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiLCAiRWFzdCBNaWRsYW5kcyIsICJXZXN0IE1pZGxhbmRzIiwgIkVhc3Qgb2YgRW5nbGFuZCIsICJMb25kb24iLCAiU291dGggRWFzdCIsIlNvdXRoIFdlc3QiLCJVSyAobm90IEVuZ2xhbmQpIiwgIk5vdCBLbm93biIpLCBzdHJpbmdzQXNGYWN0b3JzPUYpClBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wgPC0gYygiI0E2Q0VFMyIsIiMxRjc4QjQiLCIjQ0FCMkQ2IiwiIzMzQTAyQyIsIiNCMkRGOEEiLCIjRkY3RjAwIiwiI0UzMUExQyIsIiNGQjlBOTkiLCIjRDRCQjAyIiwiZ3JleTc1IiwiZ3JleTI1IikKCiMgSElWIGNvbG9yIHNjaGVtZQpQSEUuaGl2LmNvbHMgPC0gZGF0YS5mcmFtZShoaXZwb3M9cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkaGl2cG9zKSkpLCBzdHJpbmdzQXNGYWN0b3JzPUYpClBIRS5oaXYuY29scyRoaXYuY29scyA8LSBjKCIjMWY3OGI0IiwiI2IyZGY4YSIsImdyZXk3NSIpCgojIE9yaWVudGF0aW9uIGNvbG91ciBzY2hlbWUKUEhFLm9yaWVudGF0aW9uLmNvbHMgPC0gZGF0YS5mcmFtZShvcmllbnRhdGlvbj1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb24pKSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKUEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24gPC0gZmFjdG9yKFBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLCBsZXZlbHM9cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uKSkpLCBsYWJlbHM9YygiTVNXIiwiR0JNU00iLCJXU00iLCJNVW5rbm93biIsIlVua25vd24iKSkKUEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scyA8LSBjKCIjMWY3OGI0IiwiI2IyZGY4YSIsIiNmYjlhOTkiLCIjYTZjZWUzIiwiZ3JleTc1IikKCiMgVUsgYm9ybiBjb2xvdXIgc2NoZW1lClBIRS51a2Jvcm4uY29scyA8LSBkYXRhLmZyYW1lKHVrYm9ybj1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCR1a2Jvcm4pKSksdWtib3JuLmNvbHM9YygiIzFmNzhiNCIsIiNiMmRmOGEiLCJncmV5NzUiKSxzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgTG9uZG9uIGJhc2VkIGNvbG91ciBzY2hlbWUKUEhFLmxvbmRvbi5jb2xzIDwtIGRhdGEuZnJhbWUobG9uZG9uPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGxvbmRvbikpKSxsb25kb24uY29scz1jKCIjMWY3OGI0IiwiI2IyZGY4YSIsImdyZXk3NSIpLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKCiMgQWdlIGdyb3VwIGNvbG91ciBzY2hlbWUKUEhFLkFnZS5jb2xzIDwtIGRhdGEuZnJhbWUoYWdlX2dyb3VwPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGFnZV9ncm91cCkpKSxzdHJpbmdzQXNGYWN0b3JzID0gVCkKUEhFLkFnZS5jb2xzJGFnZV9ncm91cC5jb2xzIDwtIGMoYnJld2VyLnBhbChuPTQsIllsR25CdSIpLCJncmV5NzUiKQoKIyBTYW1wbGUgRGF0ZSBjb2xvdXIgc2NoZW1lClBIRS55ZWFyLmNvbHMgPC0gZGF0YS5mcmFtZSh5ZWFyPShzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJHllYXIpKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpClBIRS55ZWFyLmNvbHMkeWVhci5jb2xzIDwtIGJyZXdlci5wYWwobj03LCJZbE9yUmQiKQoKIyBTYW1wbGUgRGF0ZSAoYWxsIGdsb2JhbCBkYXRhLCBidXQgd2l0aCAxOTkwIGN1dHRvZmYpClRQQS55ZWFyLmN1dHRvZmYuY29scyA8LSBkYXRhLmZyYW1lKGRhdGUuY3V0dG9mZj1jKCI8MTk5OSIsMTk5OToyMDE5KSwgZGF0ZS5jdXR0b2ZmLmNvbD1jKCIjRjJGMkYyIixjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoNywgIllsT3JSZCIpKShsZW5ndGgoMTk5OToyMDE5KSkpKQoKCmBgYApcClwKIyMjIyMKIyMgRmlyc3QgZGVzY3JpYmUgdGhlIHNlcXVlbmNlZCBwb3B1bGF0aW9uIGFzIGEgd2hvbGUKXApTZXQgb3JkZXIgb2YgUEhFIHJlZ2lvbnMKYGBge3J9ClBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlLCBsZXZlbHM9cmV2KFBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikpCmBgYApcCkdlbmVyYXRlIHNvbWUgYmFzaWMgc3RhdGlzdGljcyBhYm91dCBnZW9ncmFwaGljYWwgUEhFIHJlZ2lvbnMgKGFub255bWlzZWQpCmBgYHtyfQpQSEUuY291bnQuYWxsIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbj1uKCkpCgpQSEUuY291bnQueWVhcnMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnllYXI9bigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZHBseXI6Om11dGF0ZShwZXJjLnBlci55ZWFyPShjb3VudC5wZXIueWVhci9zdW0oY291bnQucGVyLnllYXIpKSoxMDApCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgSElWIHN0YXR1cwpQSEUuSElWLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24pICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoaGl2cG9zKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0ID0gY3Vtc3VtKGZyYWN0aW9uKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShISVYucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgZ2VuZGVyIG9yaWVudGF0aW9uClBIRS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JSAKICBkcGx5cjo6bXV0YXRlKG9yaWVudGF0aW9uLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBVSyBib3JuICh2YWd1ZSBjYXRlZ29yeSB0aGF0J3MgdW5mb3J0dW5hdGVseSBvbmx5IG1hcmdpbmFsbHkgaGVscGZ1bCkKUEhFLlVLYm9ybi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHVrYm9ybikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JSAKICBkcGx5cjo6bXV0YXRlKFVLYm9ybi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMb25kb24gYmFzZWQKUEhFLkxvbmRvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGxvbmRvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MobG9uZG9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoTG9uZG9uLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLkFnZS5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGFnZV9ncm91cCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoYWdlX2dyb3VwKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoQWdlLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMaW5lYWdlIGdyb3VwClBIRS5MaW5lYWdlLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBX0xpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKFRQQV9MaW5lYWdlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoTGluZWFnZS5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgc3VibGluZWFnZSBncm91cApQSEUuc3VibGluZWFnZS5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEEucGluZWNvbmUuc3VibGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKFN1YmxpbmVhZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQpgYGAKXApNYWtlIHNvbWUgcGxvdHMKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQojIE1ha2UgaGJhciBwbG90IG9mIHNhbXBsZSBjb3VudHMgYnkgcmVnaW9uCnAuYWxsLmhiYXJwbG90IDwtIGdncGxvdChQSEUuY291bnQuYWxsLCBhZXMoeD1jb3VudC5wZXIucmVnaW9uLHk9IiIpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ImdyZXkzMCIpICsgCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmNvdW50LmFsbCwgYWVzKChjb3VudC5wZXIucmVnaW9uKzEyKSwgIiIsbGFiZWw9Y291bnQucGVyLnJlZ2lvbiksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgbGFicyh5PSJBbGwiLCB4PSJTYW1wbGUgQ291bnQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDI2MCkpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz00KSkgCiNwLmFsbC5oYmFycGxvdAoKIyBtYWtlIHRlbXBvcmFsIGJ1YmJsZXBsb3Qgb2YgY291bnRzIGJ5IHJlZ2lvbgpwLmFsbC55ZWFyLmJ1YmJsZXBsb3QgPC0gZ2dwbG90KFBIRS5jb3VudC55ZWFycywgYWVzKGFzLm51bWVyaWMoeWVhciksIHk9IkFsbCIpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjY1LCBhZXMoc2l6ZT1jb3VudC5wZXIueWVhcikpICsgCiAgZ2VvbV9saW5lKGFscGhhPTAuMjUpICsKICBndWlkZXMoY29sb3VyPSdub25lJykgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDcsYnJlYWtzPWMoMSw1LDEwLDI1LDUwKSkgKyAKICBndWlkZXMoc2l6ZT1ndWlkZV9sZWdlbmQobnJvdz0yKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0iZ3JleTMwIikgKyAKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgbGFicyh5PSIiLCB4PSJTYW1wbGUgWWVhciIsIHNpemU9IkNvdW50IikgCiNwLmFsbC55ZWFyLmJ1YmJsZXBsb3QKCiMgTWFrZSBwcm9wb3J0aW9uYWwgaGJhciBwbG90IG9mIEhJViBzdGF0dXMKcC5hbGwuaGl2LmhiYXJwbG90IDwtIGdncGxvdChQSEUuSElWLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1oaXZwb3MpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkhJViArdmUiLHZhbHVlcz1QSEUuaGl2LmNvbHMkaGl2LmNvbHMsIGJyZWFrcz1QSEUuaGl2LmNvbHMkaGl2cG9zKSArCiAgbGFicyh5PSJBbGwiLCB4PSJISVYgK3ZlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLkhJVi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCiNwLmFsbC5oaXYuaGJhcnBsb3QKCnAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUub3JpZW50YXRpb24uY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPWdlbmRlcl9vcmllbnRhdGlvbikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iT3JpZW50YXRpb24iLHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsKICBsYWJzKHk9IkFsbCIsIHg9Ik9yaWVudGF0aW9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90CgpwLmFsbC51a2Jvcm4uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5VS2Jvcm4uY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPXVrYm9ybikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtcbkJvcm4iLHZhbHVlcz1QSEUudWtib3JuLmNvbHMkdWtib3JuLmNvbHMsIGJyZWFrcz1QSEUudWtib3JuLmNvbHMkdWtib3JuKSArCiAgbGFicyh5PSJBbGwiLCB4PSJVSyBCb3JuIikgKwogICNndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5VS2Jvcm4uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwudWtib3JuLmhiYXJwbG90CgpwLmFsbC5Mb25kb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5Mb25kb24uY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPWxvbmRvbikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTG9uZG9uIix2YWx1ZXM9UEhFLmxvbmRvbi5jb2xzJGxvbmRvbi5jb2xzLCBicmVha3M9UEhFLmxvbmRvbi5jb2xzJGxvbmRvbikgKwogIGxhYnMoeT0iQWxsIiwgeD0iTG9uZG9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLkxvbmRvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLmFsbC5Mb25kb24uaGJhcnBsb3QKCnAuYWxsLkFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLkFnZS5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9YWdlX2dyb3VwKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJBZ2Vcbkdyb3VwIix2YWx1ZXM9UEhFLkFnZS5jb2xzJGFnZV9ncm91cC5jb2xzLCBicmVha3M9UEhFLkFnZS5jb2xzJGFnZV9ncm91cCkgKwogIGxhYnMoeT0iQWxsIiwgeD0iQWdlIEdyb3VwIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLkFnZS5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLmFsbC5BZ2UuaGJhcnBsb3QKYGBgClwKUGxvdCBjb21iaW5lZCBwbG90IGZvciAnYWxsIHNhbXBsZXMnCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9ClBIRS5hbGwuY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAuYWxsLnllYXIuYnViYmxlcGxvdCwgcC5hbGwuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuYWxsLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuYWxsLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKUEhFLmFsbC5jb21iaXBsb3QuMQpgYGAKXApcCk5leHQganVzdCBkZXNjcmliZSBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbnMgYnkgUEhFIHJlZ2lvbgpgYGB7cn0KIyBnZW5lcmF0ZSBzb21lIGJhc2ljIHN0YXRpc3RpY3MgYWJvdXQgZ2VvZ3JhcGhpY2FsIFBIRSByZWdpb25zIChhbm9ueW1pc2VkKQpQSEUuZ2VvLmNvdW50IDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb249bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLmNvdW50PXN1bShjb3VudC5wZXIucmVnaW9uKSxmcmFjdGlvbj1jb3VudC5wZXIucmVnaW9uL3RvdGFsLmNvdW50KQoKUEhFLmdlby5jb3VudC55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSx5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24ueWVhcj1uKCkpCgpQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUseWVhcixUUEFfTGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uLnllYXI9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLmNvdW50LnllYXI9c3VtKGNvdW50LnBlci5yZWdpb24ueWVhcikpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb209VFBBX0xpbmVhZ2UsIHZhbHVlc19mcm9tID0gY291bnQucGVyLnJlZ2lvbi55ZWFyKQpQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2VbaXMubmEoUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlKV0gPC0gMApQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UkeWVhciA8LSBhcy5udW1lcmljKFBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSR5ZWFyKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEhJViBzdGF0dXMKUEhFLmdlby5ISVYuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uLmhpdj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShjb3VudC5wZXIucmVnaW9uLmhpdikpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Y291bnQucGVyLnJlZ2lvbi5oaXYvdG90YWwucmVnaW9uKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGhpdnBvcyksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIERvdWJsZSBDaGVjayBISVYgc3RhdHVzIGRhdGEgZm9yIG5vbi1QSEUgZGF0YXNldCAtIGNvbmZpcm1lZCBubyBISVYrdmVzIGZyb20gbm9uLU1TTS4gClBIRS5zb3VyY2VsYWIuSElWLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoaXMuUEhFLCBnZW5kZXJfb3JpZW50YXRpb24sIGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIub3JpZW50YXRpb24uaGl2PW4oKSkgIyU+JQogICNkcGx5cjo6ZmlsdGVyKGlzLlBIRSE9IlBIRSIpCgojIEdldCB0b3RhbCBwb3B1bGF0aW9uIHN0YXRzIGZvciBISVYKUEhFLmFsbC5ISVYuY291bnRzIDwtICBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50Lmhpdj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoY291bnQudG90YWw9c3VtKGNvdW50LmhpdiksIGZyYWN0aW9uPWNvdW50Lmhpdi9jb3VudC50b3RhbCkKCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBnZW5kZXIgb3JpZW50YXRpb24KUEhFLm9yaWVudGF0aW9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKG9yaWVudGF0aW9uLmNvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShvcmllbnRhdGlvbi5wZXJjZW50PShvcmllbnRhdGlvbi5jb3VudC9zdW0ob3JpZW50YXRpb24uY291bnQpKjEwMCkpCgpQSEUuZ2VvLm9yaWVudGF0aW9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSxnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uKSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhnZW5kZXJfb3JpZW50YXRpb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1jb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uL3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUgCiAgZHBseXI6Om11dGF0ZShvcmllbnRhdGlvbi5wZXJjZW50PShjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uL3N1bShjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uKSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4KUEhFLmdlby5VS2Jvcm4gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIHVrYm9ybikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMb25kb24gYmFzZWQKUEhFLmdlby5Mb25kb24gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIGxvbmRvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MobG9uZG9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgQWdlIGdyb3VwClBIRS5nZW8uQWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCBhZ2VfZ3JvdXApICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGFnZV9ncm91cCksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExpbmVhZ2UgZ3JvdXAKUEhFLmdlby5MaW5lYWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCBUUEFfTGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBX0xpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwClBIRS5nZW8uc3VibGluZWFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCmBgYApcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9CiMgTWFrZSBoYmFyIHBsb3Qgb2Ygc2FtcGxlIGNvdW50cyBieSByZWdpb24KcC5yZWdpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uY291bnQsIGFlcyhjb3VudC5wZXIucmVnaW9uLHBoZV9jZW50cmUsIGZpbGw9cGhlX2NlbnRyZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0ic3RhY2siLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtIU0FcblJlZ2lvbiIsdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLmNvdW50LCBhZXMoKGNvdW50LnBlci5yZWdpb24rMTIpLCBwaGVfY2VudHJlLGxhYmVsPWNvdW50LnBlci5yZWdpb24pLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iU2FtcGxlIENvdW50IikgKwogICNjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMTMwKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwyNjApKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MikpIAojcC5yZWdpb24uaGJhcnBsb3QKCiMgbWFrZSB0ZW1wb3JhbCBidWJibGVwbG90IG9mIGNvdW50cyBieSByZWdpb24KcC5yZWdpb24ueWVhci5idWJibGVwbG90IDwtIGdncGxvdChQSEUuZ2VvLmNvdW50LnllYXJzLCBhZXMoYXMubnVtZXJpYyh5ZWFyKSwgcGhlX2NlbnRyZSwgY29sb3VyPXBoZV9jZW50cmUpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjY1LCBhZXMoc2l6ZT1jb3VudC5wZXIucmVnaW9uLnllYXIpKSArIAogIGdlb21fbGluZShhbHBoYT0wLjI1KSArCiAgZ3VpZGVzKGNvbG91cj0nbm9uZScpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA3LGJyZWFrcz1jKDEsNSwxMCwyNSw1MCkpICsgCiAgZ3VpZGVzKHNpemU9Z3VpZGVfbGVnZW5kKG5yb3c9MiwgZGlyZWN0aW9uID0gJ2hvcml6b250YWwnLCBieXJvdz1UKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIix2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iU2FtcGxlIFllYXIiLCBzaXplPSJDb3VudCIpIAojcC5yZWdpb24ueWVhci5idWJibGVwbG90CgojIE9yIGEgYmFycGxvdCBvZiBsaW5lYWdlIGJ5IHllYXIgJiBQSEUgcmVnaW9uPwpwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QuYmFycGxvdC5mYWNldC5saW5lYWdlIDwtIFBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSAlPiUgdGlkeXI6OnBpdm90X2xvbmdlcihjKFNTMTQsIE5pY2hvbHMpLCBuYW1lc190bz0iVFBBX0xpbmVhZ2UiLCB2YWx1ZXNfdG89IkNvdW50IikgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLCBDb3VudCwgZmlsbD1UUEFfTGluZWFnZSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCB3aWR0aD0wLjYpICsgCiAgZmFjZXRfZ3JpZChwaGVfY2VudHJlfi4sIHNjYWxlcz0nZnJlZScpICsKICBndWlkZXMoc2l6ZT1ndWlkZV9sZWdlbmQobnJvdz0yKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuTGluZWFnZSIsdmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UpICsKICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yPSd3aGl0ZScsIGZpbGw9J3doaXRlJyxsaW5ldHlwZT0ic29saWQiKSwgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXkyNSIsIHNpemU9NywgYW5nbGU9MCkpIAojcC5yZWdpb24ueWVhci5idWJibGVwbG90LmJhcnBsb3QuZmFjZXQubGluZWFnZQoKIyBNYWtlIHByb3BvcnRpb25hbCBoYmFyIHBsb3Qgb2YgSElWIHN0YXR1cwpwLnJlZ2lvbi5oaXYuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uSElWLmNvdW50cywgYWVzKGNvdW50LnBlci5yZWdpb24uaGl2LHBoZV9jZW50cmUsZmlsbD1oaXZwb3MpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iSElWICt2ZSIsdmFsdWVzPVBIRS5oaXYuY29scyRoaXYuY29scywgYnJlYWtzPVBIRS5oaXYuY29scyRoaXZwb3MpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IkhJViArdmUiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkhJVi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPWNvdW50LnBlci5yZWdpb24uaGl2KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCiNwLnJlZ2lvbi5oaXYuaGJhcnBsb3QKCnAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24scGhlX2NlbnRyZSxmaWxsPWdlbmRlcl9vcmllbnRhdGlvbikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJPcmllbnRhdGlvbiIsdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iT3JpZW50YXRpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Y291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbiksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QKCnAucmVnaW9uLnVrYm9ybi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5VS2Jvcm4sIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9dWtib3JuKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLIEJvcm4iLHZhbHVlcz1QSEUudWtib3JuLmNvbHMkdWtib3JuLmNvbHMsIGJyZWFrcz1QSEUudWtib3JuLmNvbHMkdWtib3JuKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJVSyBCb3JuIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5VS2Jvcm4sIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi51a2Jvcm4uaGJhcnBsb3QKCnAucmVnaW9uLkxvbmRvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5Mb25kb24sIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9bG9uZG9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkxvbmRvbiIsdmFsdWVzPVBIRS5sb25kb24uY29scyRsb25kb24uY29scywgYnJlYWtzPVBIRS5sb25kb24uY29scyRsb25kb24pICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IkxvbmRvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uTG9uZG9uLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24uTG9uZG9uLmhiYXJwbG90CgpwLnJlZ2lvbi5BZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uQWdlLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPWFnZV9ncm91cCkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJBZ2Vcbkdyb3VwIix2YWx1ZXM9UEhFLkFnZS5jb2xzJGFnZV9ncm91cC5jb2xzLCBicmVha3M9UEhFLkFnZS5jb2xzJGFnZV9ncm91cCkgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iQWdlIEdyb3VwIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTEpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5BZ2UsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi5BZ2UuaGJhcnBsb3QKYGBgClwKQ29tYmluZWQgcGxvdApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQpQSEUucmVnaW9uLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QsIHAucmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDQsMiwyLDIsMiksIHNjYWxlPTAuOSkKClBIRS5yZWdpb24uY29tYmlwbG90LjEKYGBgCgoKXApSZWdpb25zIGFzIGEgY29tcGxleCBtdWx0aXBhbmVsIHBsb3QKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LjV9CgoKIyBsZWdlbmRzClBIRS5yZWdpb24uY29tYmlwbG90LjEubGVnZW5kcyA8LSBwbG90X2dyaWQoZ2V0X2xlZ2VuZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QpLCBnZXRfbGVnZW5kKHAucmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnJlZ2lvbi5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnJlZ2lvbi5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg2LDQsNCw0LDQpLCBzY2FsZT0wLjk1KQoKCiMgQXJyYW5nZSBwbG90cyB2ZXJ0aWNhbGx5CnAueWVhci5idWJibGVwbG90LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC55ZWFyLmJ1YmJsZXBsb3QgKyB4LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5yZWdpb24uaGJhci5jb3VudHMuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLm9yaWVudGF0aW9uLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5yZWdpb24uaGJhci5oaXYuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLmhpdi5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIuQWdlLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5BZ2UuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgojIENvbWJpbmUgdGhlIHBsb3RzCnAucmVnaW9uLmhiYXIuY29tYmkucGx1cy5hbGwgPC0gcGxvdF9ncmlkKHAueWVhci5idWJibGVwbG90LmNvbWJpLCBwLnJlZ2lvbi5oYmFyLmNvdW50cy5jb21iaSwgcC5yZWdpb24uaGJhci5vcmllbnRhdGlvbi5jb21iaSwgcC5yZWdpb24uaGJhci5oaXYuY29tYmksIHAucmVnaW9uLmhiYXIuQWdlLmNvbWJpLCBucm93PTEsIHJlbF93aWR0aHM9Yyg2LDQsNCw0LDQpLCBsYWJlbHMgPSBjKCJBIiwiQiIsIkMiLCJEIiwiRSIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCB2anVzdD0wLjI1KQojIGFuZCBhZGQgdGhlIGxlZ2VuZHMgb24gdG9wCnAucmVnaW9uLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsLCBQSEUucmVnaW9uLmNvbWJpcGxvdC4xLmxlZ2VuZHMsIG5jb2w9MSwgcmVsX2hlaWdodHM9Yyg2LDEpLCBzY2FsZSA9IDAuOTUpCgoKCnAucmVnaW9uLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnMl9UUEEtUEhFX1NhbXBsZS1tZXRhZGlzdHJvcy1ieS1waGVfcmVnaW9uK2FsbC1jb21iaS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0yNDAsIGhlaWdodD0xMzUsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKXApcCk5vdyBsZXRzIGxvb2sgYXQgc29tZSBnZW5ldGljIGRhdGEKXAojIyMgTWFrZSBNTCB0cmVlIHdpdGggc3VibGluZWFnZSB0aXBwb2ludHMKYGBge3J9ClRQQS5NTHRyZWUuZ2d0cmVlLnRpcHBvaW50IDwtIFRQQS5NTHRyZWUuZ2d0cmVlICU8KyUgZGF0YS5mcmFtZShTYW1wbGVfTmFtZT1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgU3VibGluZWFnZT1UUEEubWV0YTIuMSRUUEEucGluZWNvbmUuc3VibGluZWFnZSxzdHJpbmdzQXNGYWN0b3JzID0gRikgKyAKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvcj1TdWJsaW5lYWdlKSwgc2l6ZT0wLjUsIGFscGhhPTAuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKYGBgClwKQWRkIG1ldGFkYXRhCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CiMgQ29udGluZW50CnAuVFBBLk1MdHJlZS5QSEUgPC0gZ2hlYXRtYXAoVFBBLk1MdHJlZS5nZ3RyZWUudGlwcG9pbnQsCiAgICAgICAgICAgICAgIFRQQS5yYXdzZXEuY29udGluZW50cy5wLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAwMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJDb250aW5lbnQiLHZhbHVlcz1jb250aW5lbnRhbC5jb2xzLmJyZXcyJGNvbnRpbmVudC5jb2wsIGJyZWFrcz1jb250aW5lbnRhbC5jb2xzLmJyZXcyJENvbnRpbmVudCwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxLG5jb2w9MikpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIG5ld19zY2FsZV9maWxsKCkKCiMgaXMgVUsKcC5UUEEuTUx0cmVlLlBIRSA8LSBnaGVhdG1hcChwLlRQQS5NTHRyZWUuUEhFLAogICAgICAgICAgICAgICBUUEEucmF3c2VxLlVLLnAsIGNvbG9yPU5VTEwsd2lkdGg9MC4wNzUsb2Zmc2V0PTAuMDAwMDEwMjUsIGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkVuZ2xhbmQvT3RoZXIiLCB2YWx1ZXM9YygiYmxhY2siLCJncmV5OTUiKSwgYnJlYWtzPWMoIkVuZ2xhbmQiLCJPdGhlciIpLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIsbmNvbD0yKSkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgbmV3X3NjYWxlX2ZpbGwoKQoKIyBMaW5lYWdlCnAuVFBBLk1MdHJlZS5QSEUgPC0gZ2hlYXRtYXAocC5UUEEuTUx0cmVlLlBIRSxUUEEucmF3c2VxLkxpbmVhZ2UucCwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMjAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTGluZWFnZSIsdmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMywgbmNvbD0yKSkgKyB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogICBuZXdfc2NhbGVfZmlsbCgpICsKICBOVUxMCgojIHN1YmxpbmVhZ2UKcC5UUEEuTUx0cmVlLlBIRSA8LSBnaGVhdG1hcChwLlRQQS5NTHRyZWUuUEhFLCBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgU3VibGluZWFnZT1UUEEubWV0YTIuMSRUUEEucGluZWNvbmUuc3VibGluZWFnZSxzdHJpbmdzQXNGYWN0b3JzID0gRiksIGNvbG9yPU5VTEwsd2lkdGg9MC4wNzUsb2Zmc2V0PTAuMDAwMDMwMjUsIGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNCwgbmNvbD0zKSkgKyB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogICBuZXdfc2NhbGVfZmlsbCgpICsKICBOVUxMCmBgYApcCnBsb3QKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KcC5UUEEuTUx0cmVlLlBIRQoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWczX1RQQS1QSEVfR2xvYmFsX1BoeWxvK1VLLWhpZ2hsaWdodHMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTg1LCBoZWlnaHQ9MTYwLCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKXApcCiMjIyBHZW9ncmFwaGljIGRpc3RyaWJ1dGlvbnMgb2YgTGluZWFnZXMgYW5kIFN1YmxpbmVhZ2VzCldoYXQgYWJvdXQgc3VibGluZWFnZXM/CmBgYHtyfQpwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkxpbmVhZ2UsIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9VFBBX0xpbmVhZ2UpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5MaW5lYWdlIix2YWx1ZXM9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCwgYnJlYWtzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSkgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iVFBBIExpbmVhZ2UiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICAjZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5MaW5lYWdlLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgTlVMTAoKcC5yZWdpb24uc3VibGluZWFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5zdWJsaW5lYWdlLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iVFBBIFN1YmxpbmVhZ2UiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9NCkpICsKICAjZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5zdWJsaW5lYWdlLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgTlVMTAoKYGBgClwKQ29tYmkgcGxvdCAoZ2VvZ3JhcGh5IGxpbmVhZ2VzKQpgYGB7cn0KUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcyA8LSBwbG90X2dyaWQocC5yZWdpb24ueWVhci5idWJibGVwbG90ICtsZWdlbmQuc3RyaXAsIHAucmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCArIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwxNTApKSwgcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgK2xlZ2VuZC5zdHJpcCwgcC5yZWdpb24uc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgK2xlZ2VuZC5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg2LDMsNCw0KSwgc2NhbGU9MC45OSwgbGFiZWxzPWMoIkMiLCJEIiwiRSIsIkYiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSkKCiMgc2VwYXJhdGUgb3V0IHRoZSBwbG90IGZvciB0aGUgbGVnZW5kcwpwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QubGVnZW5kIDwtIGdldF9sZWdlbmQocC5yZWdpb24ueWVhci5idWJibGVwbG90KQpwLnJlZ2lvbi5oYmFycGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApCnAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QubGVnZW5kIDwtIGdldF9sZWdlbmQocC5yZWdpb24uTGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApCnAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QubGVnZW5kIDwtIGdldF9sZWdlbmQocC5yZWdpb24uc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApCgpQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzLmxlZ2VuZCA8LSBwbG90X2dyaWQocC5yZWdpb24ueWVhci5idWJibGVwbG90LmxlZ2VuZCwgcC5yZWdpb24uaGJhcnBsb3QubGVnZW5kLCBwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCwgcC5yZWdpb24uc3VibGluZWFnZS5oYmFycGxvdC5sZWdlbmQsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiwzLDQsNCkpCgpQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzIDwtIHBsb3RfZ3JpZChQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzLCBQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzLmxlZ2VuZCwgcmVsX2hlaWdodHMgPSBjKDQsMSksIG5jb2w9MSkKClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMKYGBgClwKT0ssIGxldCdzIG5vdyBhZGQgYSBtYXAgb2YgdGhlc2UgZ2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbnMKClwKTGV0J3MgdXNlZCBPTlMgcHVibGlzaGVkIHNoYXBlIGZpbGVzIC0gdGhlcmUgaXMgb25lIGF2YWlsYWJsZSB0aGF0IHNob3dzIFB1YmxpYyBIZWFsdGggRW5nbGFuZCByZWdpb24gYm91bmRhcmllcy4gCmBgYHtyfQoKIyBHZW5lcmF0ZSBhcHByb3hpbWF0ZSByZWdpb25hbCBHUFMgY29vcmRzClBIRS5yZWdpb24uR1BTIDwtIGRhdGEuZnJhbWUoCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgcGhlX2NlbnRyZSA9IGMoIkVhc3QgTWlkbGFuZHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIkVhc3Qgb2YgRW5nbGFuZCIsIkxvbmRvbiIsIk5vcnRoIEVhc3QiLCJOb3J0aCBXZXN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICJTb3V0aCBFYXN0IiwiU291dGggV2VzdCIsIldlc3QgTWlkbGFuZHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIllvcmtzaGlyZSBhbmQgSHVtYmVyIiwiVUsgKG5vdCBFbmdsYW5kKSIsIk5vdCBLbm93biIpLAogICAgICAgICAgICBMb25naXR1ZGUgPSBjKC0wLjcsMC41LC0wLjIsLTEuOSwtMi40LAogICAgICAgICAgICAgICAgICAgICAgICAgMC4wNSwtMi45LC0yLC0wLjgsMC4xLDAuNjMpLAogICAgICAgICAgIExhdGl0dWRlID0gYyg1Mi45LDUyLjQsNTEuNSw1NSw1My43LAogICAgICAgICAgICAgICAgICAgICAgICAgNTEuMSw1MSw1Mi42LDUzLjgsNTQuNyw1NC4xKQogICkgIApQSEUucmVnaW9uLkdQUyA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMsIFBIRS5nZW8uTGluZWFnZVtQSEUuZ2VvLkxpbmVhZ2UkVFBBX0xpbmVhZ2U9PSJTUzE0IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMpWzRdIDwtICJTUzE0IgpQSEUucmVnaW9uLkdQUyA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMsIFBIRS5nZW8uTGluZWFnZVtQSEUuZ2VvLkxpbmVhZ2UkVFBBX0xpbmVhZ2U9PSJOaWNob2xzIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMpWzVdIDwtICJOaWNob2xzIgpQSEUucmVnaW9uLkdQU1tpcy5uYShQSEUucmVnaW9uLkdQUyldIDwtIDAKClBIRS5yZWdpb24uR1BTIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgUEhFLmdlby5MaW5lYWdlW1BIRS5nZW8uTGluZWFnZSRUUEFfTGluZWFnZT09IlNTMTQiLGMoInBoZV9jZW50cmUiLCJ0b3RhbC5yZWdpb24iKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMpWzZdIDwtICJSZWdpb25fQ291bnQiCgpQSEUucmVnaW9uLkdQUyRyYWRpdXMgPC0gMC41KigxLTEvc3FydChQSEUucmVnaW9uLkdQUyRSZWdpb25fQ291bnQpKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbXBvcnQgZGF0YWZpbGUgZnJvbSBodHRwczovL2dlb3BvcnRhbC5zdGF0aXN0aWNzLmdvdi51ay9kYXRhc2V0cy9wdWJsaWMtaGVhbHRoLWVuZ2xhbmQtY2VudHJlcy1kZWNlbWJlci0yMDE2LWZ1bGwtY2xpcHBlZC1ib3VuZGFyaWVzLWluLWVuZ2xhbmQvZXhwbG9yZT9sb2NhdGlvbj01Mi45NTAwMDAlMkMtMi4wMDAwMDAlMkM2Ljg4CgpVSy5zaGFwZWZpbGUgPC0gcmVhZE9HUihkc249VUsucHVibGljaGVhbHRoLnNoYXBlZmlsZS5kYXRhKQoKI1Jlc2hhcGUgZm9yIGdncGxvdDIgdXNpbmcgdGhlIEJyb29tIHBhY2thZ2UKVUsubWFwZGF0YSA8LSB0aWR5KFVLLnNoYXBlZmlsZSwgcmVnaW9uPSJwaGVjMTZubSIpCgojVUsuZ2cgPC0gZ2dwbG90KCkgKyBnZW9tX3BvbHlnb24oZGF0YSA9IFVLLm1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGNvbG9yID0gIiNGRkZGRkYiLCBzaXplID0gMC4yNSkKVUsuZ2cgPC0gZ2dwbG90KCkgKyBnZW9tX3BvbHlnb24oZGF0YSA9IFVLLm1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGNvbG9yPSJncmV5MjUiLCBmaWxsPSJncmV5OTAiLCBzaXplID0gMC4wNzUpCgojVUsuZ2cgPC0gVUsuZ2cgKyBjb29yZF9maXhlZCgxKSArIHRoZW1lX25vdGhpbmcoKQojVUsuZ2cKIyBNYXAgcGxvdHRpbmcgZmlsZSBiZWNvbWVzIF92ZXJ5XyBiaWcgLSB1c2UgZ2dyYXN0ciB0byByZWR1Y2UgdGhlIHNpemUKVUsuZ2cgPC1nZ3Bsb3QoKSArIGdncmFzdHI6OnJhc3RlcmlzZShnZW9tX3BvbHlnb24oZGF0YSA9IFVLLm1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGNvbG9yPSJncmV5MjUiLCBmaWxsPSJncmV5OTAiLCBzaXplID0gMC4wNzUpLCBkcGk9NDAwKSArIGNvb3JkX2ZpeGVkKDEpICsgdGhlbWVfbm90aGluZygpCgojcmFzdGVyaXNlKGdlb21fcG9pbnQoYWVzKGNhcmF0LCBwcmljZSwgY29sb3VyID0gY3V0KSwgZGF0YT1kaWFtb25kcyksIGRwaT0zMCkKCgoKIyBDb252ZXJ0IFVLIHJlZ2lvbnMgdG8gYmUgY29tcGF0aWJsZSB3aXRoIG1hcAojIEZpcnN0IGZpbmQgY2VudHJlIHBvaW50IGZvciBlYWNoIHJlZ2lvbgpVSy5tYXBkYXRhLnJlZ2lvbnMubWVhbmNvb3JkcyA8LSBVSy5tYXBkYXRhICU+JSBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UobWVhbi5sYXQ9bWVhbihsYXQpLCBtZWFuLmxvbmc9bWVkaWFuKGxvbmcpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpCmNvbG5hbWVzKFVLLm1hcGRhdGEucmVnaW9ucy5tZWFuY29vcmRzKVsxXSA8LSAicGhlX2NlbnRyZSIKClBIRS5yZWdpb24uR1BTLnVrbWFwIDwtIGRwbHlyOjpsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMsIFVLLm1hcGRhdGEucmVnaW9ucy5tZWFuY29vcmRzLCBieT0icGhlX2NlbnRyZSIpCgojIEFkZCBhcnRpZmljaWFsIGxvY2F0aW9uIGZvciAnbm90IGtub3duJwpQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iTm90IEtub3duIiwibWVhbi5sYXQiXSA8LSA2MDAwMDAKUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsIm1lYW4ubG9uZyJdIDwtIDU1MDAwMAoKIyBTaGlmdCAiU291dGggRWFzdCIgc2xpZ2h0bHkgdG8gcmVkdWNlIHRoZSBvdmVybGFwIHdpdGggTG9uZG9uClBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmU9PSJTb3V0aCBFYXN0IiwibWVhbi5sb25nIl0gPC0gNDc1MDAwCiMgU2hpZnQgIkVhc3Qgb2YgRW5nbGFuZCBFYXN0IiBzbGlnaHRseSB0byByZWR1Y2UgdGhlIG92ZXJsYXAgd2l0aCBMb25kb24gClBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmU9PSJFYXN0IG9mIEVuZ2xhbmQiLCJtZWFuLmxhdCJdIDwtIDI3NTAwMAoKIyBOb3QgZ29pbmcgdG8gdHJ5IHBsb3R0aW5nIHRoZSAyIHNhbXBsZXMgZnJvbSBlbHNld2hlcmUgaW4gdGhlIFVLLCBzbyByZW1vdmUgdGhhdCByb3cKUEhFLnJlZ2lvbi5HUFMudWttYXAgPC0gUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZSAhPSAiVUsgKG5vdCBFbmdsYW5kKSIsXQoKIyBDcmVhdGUgcmFkaXVzIHZhcmlhYmxlIGZvciBwbG90dGluZyBwaWUgc2l6ZXMgKHVzZSBsb2cxMChuKSoyMCwwMDApClBIRS5yZWdpb24uR1BTLnVrbWFwJHJhZGl1cy5VSyA8LSBsb2cxMChQSEUucmVnaW9uLkdQUy51a21hcCRSZWdpb25fQ291bnQpKjIwMDAwCgojUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlCgpVSy5nZy5zY2F0dGVycGllIDwtIFVLLmdnICsgZ2VvbV9zY2F0dGVycGllKGRhdGE9UEhFLnJlZ2lvbi5HUFMudWttYXAsIGFlcyhtZWFuLmxvbmcsIG1lYW4ubGF0LCBncm91cD1waGVfY2VudHJlLCByPXJhZGl1cy5VSyksIGFscGhhPTAuODUsIGNvbG9yPU5BLCBjb2xzPWMoIk5pY2hvbHMiLCJTUzE0IikpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5MaW5lYWdlIix2YWx1ZXM9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCwgYnJlYWtzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIpCgpVSy5nZy5zY2F0dGVycGllIDwtIFVLLmdnLnNjYXR0ZXJwaWUgKyBnZW9tX3NjYXR0ZXJwaWVfbGVnZW5kKFBIRS5yZWdpb24uR1BTLnVrbWFwWyFpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcCRtZWFuLmxhdCksInJhZGl1cy5VSyJdLCBsYWJlbGxlcj1mdW5jdGlvbih4KSByb3VuZCgoMTBeKHgvMjAwMDApKSwwKSwgbj0zLCB4PTE1MDAwMCwgeT01MDAwMDApCgpVSy5nZy5zY2F0dGVycGllIDwtIFVLLmdnLnNjYXR0ZXJwaWUgKyB0aGVtZV9ub3RoaW5nKCkKCiM/IEFkZCBsYWJlbHMKVUsuZ2cuc2NhdHRlcnBpZS5sYWJzIDwtIFVLLmdnLnNjYXR0ZXJwaWUgKyBnZW9tX2xhYmVsX3JlcGVsKGRhdGE9UEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSxdLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgbGFiZWw9cGhlX2NlbnRyZSksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgbnVkZ2VfeCA9IDUwMDAwLCBudWRnZV95ID0gLTI1MDAwLCBzZWdtZW50LnNpemUgID0gMC4xKSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgCiAgdGhlbWUudGV4dC5zaXplICsKICB0aGVtZV9ub3RoaW5nKCkKClVLLmdnLnNjYXR0ZXJwaWUubGFicwpgYGAKXApcCk5vdyBkbyBhbiBlcXVpdmFsZW50IHBsb3QgZm9yIHN1YmxpbmVhZ2VzCmBgYHtyfQpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gUEhFLnJlZ2lvbi5HUFMudWttYXAKCgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjEiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzExXSA8LSAiMSIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIyIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxMl0gPC0gIjIiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMyIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTNdIDwtICIzIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjYiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE0XSA8LSAiNiIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSI4IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxNV0gPC0gIjgiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMTQiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE2XSA8LSAiMTQiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMTUiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE3XSA8LSAiMTUiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMTYiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE4XSA8LSAiMTYiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iU2luZ2xldG9uIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxOV0gPC0gIlNpbmdsZXRvbiIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluW2lzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbildIDwtIDAKCiMgTW9zdCBzYW1wbGVzIGFyZSBlaXRoZXIgc3VibGluZWFnZSAxIG9yIDE0LiBMZXQncyBjcmVhdGUgYSBjb3VudCBvZiBzYW1wbGVzIHRoYXQgYXJlIG5laXRoZXIuClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiRgT3RoZXIgU3VibGluZWFnZXNgIDwtIHNhcHBseSgxOm5yb3coUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKSwgZnVuY3Rpb24gKHgpIFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiRSZWdpb25fQ291bnRbeF0tc3VtKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiRgMWBbeF0sIFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiRgMTRgW3hdKSkgCgoKClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSA8LSBVSy5nZyArIGdlb21fc2NhdHRlcnBpZShkYXRhPVBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbltQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kbWVhbi5sb25nIT0wLF0sIGFlcyhtZWFuLmxvbmcsIG1lYW4ubGF0LCBncm91cD1waGVfY2VudHJlLCByPXJhZGl1cy5VSyksIGFscGhhPTAuODUsIGNvbG9yPU5BLCBjb2xzPWMoIjEiLCIxNCIsIk90aGVyIFN1YmxpbmVhZ2VzIikpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9YygiI0ZDOTI3MiIsIiNCQ0JEREMiLCAiZ3JleTUwIiksIGJyZWFrcz1jKCIxIiwiMTQiLCJPdGhlciBTdWJsaW5lYWdlcyIpKQoKIyBhZGQgbGVnZW5kClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSA8LSBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgKyBnZW9tX3NjYXR0ZXJwaWVfbGVnZW5kKFBIRS5yZWdpb24uR1BTLnVrbWFwWyFpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcCRtZWFuLmxhdCksInJhZGl1cy5VSyJdLCBsYWJlbGxlcj1mdW5jdGlvbih4KSByb3VuZCgoMTBeKHgvMjAwMDApKSwwKSwgbj0zLCB4PTE1MDAwMCwgeT01MDAwMDApCgojVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZy5zY2F0dGVycGllICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSArIHRoZW1lX25vdGhpbmcoKQoKIz8gQWRkIGxhYmVscwpVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlICsgZ2VvbV9sYWJlbF9yZXBlbChkYXRhPVBIRS5yZWdpb24uR1BTLnVrbWFwWyFpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcCRtZWFuLmxhdCksXSwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGxhYmVsPXBoZV9jZW50cmUpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIG51ZGdlX3ggPSA1MDAwMCwgbnVkZ2VfeSA9IC0yNTAwMCwgc2VnbWVudC5zaXplICA9IDAuMSkgKwogIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgCiAgdGhlbWUudGV4dC5zaXplICsKICB0aGVtZV9ub3RoaW5nKCkKCgpVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UKYGBgCgpcCkNvbWJpbmVkIG1hcCBwbG90CmBgYHtyfQpVSy5nZy5zY2F0dGVycGllLmNvbWJpIDwtIHBsb3RfZ3JpZChVSy5nZy5zY2F0dGVycGllLmxhYnMsIFVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSwgbmNvbD0yLCBsYWJlbHMgPSBjKCJBIiwiQiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKVUsuZ2cuc2NhdHRlcnBpZS5jb21iaQpgYGAKXApcClBsb3QgaW4gY29tYmluYXRpb24gd2l0aCBiYXJwbG90cwpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OH0KcGxvdF9ncmlkKFVLLmdnLnNjYXR0ZXJwaWUuY29tYmksIFBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMsIG5yb3c9MiwgcmVsX2hlaWdodHM9Yyg0LDUpKQoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzJfVFBBLVBIRV9NYXAtTGluZWFnZStCYXJwbG90cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xOTAsIGhlaWdodD0xODUsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYApcClwKIyMjIEFuYWx5c2lzIGJ5IHN1YmxpbmVhZ2UKXApOb3cgbGV0cyBzdGFydCBleHBsb3JpbmcgaG93IHNhbXBsZXMgYXJlIGRpc3RyaWJ1dGVkIGJ5IHN1YmxpbmVhZ2UKCmBgYHtyfQpQSEUubWV0YWRhdGEubGlua2VkIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQKUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbGV2ZWxzPXJldihhcy5jaGFyYWN0ZXIoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSkpKSkpCgpQSEUuTGluZWFnZS5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBX0xpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsPXN1bShDb3VudCksIHBlcmM9KENvdW50L3RvdGFsKSoxMDApCgpQSEUuc3VibGluLmNvdW50IDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWw9c3VtKENvdW50KSwgcGVyYz0oQ291bnQvdG90YWwpKjEwMCkKClBIRS5nZW8uc3VibGluLnllYXJzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSx5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkKCgojIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IHN1YmxpbmVhZ2UgZ3JvdXBzCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgZ2VuZGVyIG9yaWVudGF0aW9uClBIRS5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhnZW5kZXJfb3JpZW50YXRpb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgVUsgYm9ybgpQSEUuc3VibGluZWFnZS5VS2Jvcm4gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB1a2Jvcm4pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICAjZHBseXI6OmFycmFuZ2UoZGVzYyh1a2Jvcm4pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh1a2Jvcm4pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExvbmRvbiBiYXNlZApQSEUuc3VibGluZWFnZS5Mb25kb24gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBsb25kb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGxvbmRvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEFnZSBncm91cApQSEUuc3VibGluZWFnZS5BZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBhZ2VfZ3JvdXApICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGFnZV9ncm91cCksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEhJViBncm91cApQSEUuc3VibGluZWFnZS5ISVYgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGhpdnBvcyksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KUEhFLnN1YmxpbmVhZ2UuUEhFY2VudHJlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgcGhlX2NlbnRyZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MocGhlX2NlbnRyZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKYGBgCgpcClBsb3QgYnkgc3VibGluZWFnZQpgYGB7cn0KcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5zdWJsaW4ueWVhcnMsIGFlcyhhcy5udW1lcmljKHllYXIpLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgY29sb3VyPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC42NSwgYWVzKHNpemU9Q291bnQpKSArIAogIGdlb21fbGluZShhbHBoYT0wLjI1KSArCiAgZ3VpZGVzKGNvbG91cj0nbm9uZScpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA3LGJyZWFrcz1jKDEsNSwxMCwyNSw1MCkpICsgCiAgZ3VpZGVzKHNpemU9Z3VpZGVfbGVnZW5kKG5yb3c9MiwgZGlyZWN0aW9uID0gJ2hvcml6b250YWwnLCBieXJvdz1UKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJUUEFcblN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iU2FtcGxlIFllYXIiLCBzaXplPSJDb3VudCIpIAojcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdAoKcC5zdWJsaW5lYWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluLmNvdW50LCBhZXMoQ291bnQsVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsZmlsbD1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0ic3RhY2siLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlNhbXBsZSBDb3VudCIpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluLmNvdW50LCBhZXMoKENvdW50KzEyKSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogICNjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjAwKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwyNjApKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MikpCiNwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgCgpwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UseD1Db3VudCxmaWxsPWdlbmRlcl9vcmllbnRhdGlvbikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJPcmllbnRhdGlvbiIsdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJPcmllbnRhdGlvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdAoKcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuSElWLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSwgeD1Db3VudCxmaWxsPWhpdnBvcykpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJISVYgK3ZlIix2YWx1ZXM9UEhFLmhpdi5jb2xzJGhpdi5jb2xzLCBicmVha3M9UEhFLmhpdi5jb2xzJGhpdnBvcykgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJISVYgK3ZlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTEpKSArIAogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLkhJViwgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90CgpwLnN1YmxpbmVhZ2UudWtib3JuLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5VS2Jvcm4sIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHg9Q291bnQsZmlsbD11a2Jvcm4pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtcbmJvcm4iLHZhbHVlcz1QSEUudWtib3JuLmNvbHMkdWtib3JuLmNvbHMsIGJyZWFrcz1QSEUudWtib3JuLmNvbHMkdWtib3JuKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlVLIGJvcm4iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5VS2Jvcm4sIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdAoKcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuQWdlLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSwgeD1Db3VudCAsZmlsbD1hZ2VfZ3JvdXApKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQWdlXG5Hcm91cCIsdmFsdWVzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scywgYnJlYWtzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXApICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iQWdlIEdyb3VwIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTEpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2UuQWdlLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3Auc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QKCgpwLnN1YmxpbmVhZ2UuUEhFcmVnaW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5QSEVjZW50cmUsIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB4PUNvdW50LCBmaWxsPXBoZV9jZW50cmUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtIU0FcblJlZ2lvbiIsdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRQSEUucmVnaW9uKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlVLSFNBIFJlZ2lvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz00KSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLlBIRWNlbnRyZSwgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCgpgYGAKXApMb29rIGF0IGhvdyBzdWJsaW5lYWdlcyBhcmUgZGlzdHJpYnV0ZWQgYnkgcmVnaW9uIChzdWJsaW5lYWdlLWNlbnRyaWMpCmBgYHtyfQpwLnN1YmxpbmVhZ2UuUEhFcmVnaW9uLmhiYXJwbG90CmBgYAoKXApDb21iaW5lIHBhdGllbnQgbWV0YWRhdGEgaW50byBhIHBsb3QKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0KI1BIRS5zdWJsaW5lYWdlcy5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCwgcC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5QSEVyZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UudWtib3JuLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoMywyLDIsMiwyLDIsMiksIHNjYWxlPTAuOSkKCiNQSEUuc3VibGluZWFnZXMuY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoMywyLDIsMiwyLDQpLCBzY2FsZT0wLjkpCgpQSEUuc3VibGluZWFnZXMuY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgpQSEUuc3VibGluZWFnZXMuY29tYmlwbG90LjEgCgpgYGAKCgpcCkxldHMgYWRkIHRoZSAnYWxsJyByb3cgYWdhaW4gdG8gdGhlICdieSBzdWJsaW5lYWdlJyBwbG90CmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMn0KIyBsZWdlbmRzClBIRS5zdWJsaW5lYWdlLmNvbWJpcGxvdC4xLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKGdldF9sZWdlbmQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCksIGdldF9sZWdlbmQocC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg2LDQsNCw0LDQpLCBzY2FsZT0wLjk1KQoKIyByZWdpb25zCiNQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5ub2xlZ2VuZCA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCArIGxlZ2VuZC5zdHJpcCwgcC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDQsMiwyLDIsMiksIHNjYWxlPTAuOSkKCiMgT3IgZG8gaXQgdmVydGljYWxseQpwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC55ZWFyLmJ1YmJsZXBsb3QgKyB4LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90ICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAuc3VibGluZWFnZS5oYmFyLmNvdW50cy5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnN1YmxpbmVhZ2UuaGJhci5vcmllbnRhdGlvbi5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnN1YmxpbmVhZ2UuaGJhci5oaXYuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLmhpdi5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnN1YmxpbmVhZ2UuaGJhci5BZ2UuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLkFnZS5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgojIENvbWJpbmUgdGhlIHBsb3RzCnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLm9yaWVudGF0aW9uLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5oaXYuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLkFnZS5jb21iaSwgbnJvdz0xLCByZWxfd2lkdGhzPWMoNywzLDQsNCw0KSwgbGFiZWxzPWMoIkEiLCAiQiIsICJDIiwgIkQiLCAiRSIpLGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHZqdXN0PTEsIHNjYWxlPTAuOTkpCgojIGFuZCBhZGQgdGhlIGxlZ2VuZHMgb24gdG9wCiNwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKFBIRS5zdWJsaW5lYWdlLmNvbWJpcGxvdC4xLmxlZ2VuZHMsIHAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoMSw5KSkKCiMgbGVnZW5kcyBiZWxvdwpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLCBQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoOCwxKSkKCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMKCmBgYAoKXApcIApUaGVzZSBwYXR0ZXJucyBsb29rIGZhaXJseSBzaW1pbGFyIGJldHdlZW4gc3VibGluZWFnZXMsIGFuZCAoYXBhcnQgZnJvbSAxICYgMTQpIHRoZSBncm91cHMgYXJlIHZlcnkgc21hbGwuIEhvd2V2ZXIsIHN1YmxpbmVhZ2UgMTQgZG9lcyBhcHBlYXIgdG8gaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIE1TTSBjb21wYXJlZCB0byBzdWJsaW5lYWdlIDEgYW5kIG90aGVycy4gTGV0J3MgdGVzdCB0aGF0IGZvcm1hbGx5IHVzaW5nIDJ4MiBmaXNoZXIncyB0ZXN0cwpcCmBgYHtyfQpQSEUuTVNNLmNvdW50cy5hbGwgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGlzLk1TTSwgLmRyb3A9RikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKChpcy5NU00pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGlzLk1TTSwgLmRyb3A9RikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKChpcy5NU00pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgIyU+JQogICNkcGx5cjo6ZmlsdGVyKCFpcy5uYShpcy5NU00pKQoKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIgPC0gUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cyAlPiUgZHBseXI6OnNlbGVjdChUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaXMuTVNNLCBDb3VudCkgJT4lCiAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBpcy5NU00sIHZhbHVlc19mcm9tPUNvdW50KSAlPiUKICBkcGx5cjo6bXV0YXRlKE1TTT1yZXBsYWNlX25hKE1TTSwgMCksIE90aGVyPXJlcGxhY2VfbmEoT3RoZXIsIDApLCBUb3RhbD1zdW0oTVNNLE90aGVyKSkgJT4lCiAgI2RwbHlyOjpzZWxlY3QoLWBOQWApICU+JQogIGRwbHlyOjpmaWx0ZXIoVG90YWwhPTApCiAgCgpQSEUuc3VibGluZWFnZS5NU00ucHZhbCA8LSBkYXRhLmZyYW1lKFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPVBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHAuZmlzaGVyPXNhcHBseSgxOm5yb3coUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlciksIGZ1bmN0aW9uICh4KSBmaXNoZXIudGVzdChtYXRyaXgoYXMubnVtZXJpYyhjKFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXJbeCwiTVNNIl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlclt4LCJPdGhlciJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBIRS5NU00uY291bnRzLmFsbFtQSEUuTVNNLmNvdW50cy5hbGwkaXMuTVNNPT0iTVNNIiwiQ291bnQiXSwgUEhFLk1TTS5jb3VudHMuYWxsW1BIRS5NU00uY291bnRzLmFsbCRpcy5NU009PSJPdGhlciIsIkNvdW50Il0pKSxucm93PTIpKVtbMV1dKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQoKUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlciA8LSBkcGx5cjo6bGVmdF9qb2luKFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIsIFBIRS5zdWJsaW5lYWdlLk1TTS5wdmFsLCBieT0iVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiKQoKUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlcgpgYGAKCgpcClwKIyMjIFZpc3VhbGlzYXRpb24gb2YgVUsgZ2Vub21pYyByZWxhdGlvbnNoaXBzClwKT2ssIGxldCdzIG1ha2UgYSB0cmVlIGZvciBkaXNwbGF5aW5nIHRoZXNlIHJlbGF0aW9uc2hpcHMgdXNpbmcgdGhlIFVLIGRhdGFzZXQgb25seQpcCkZyb20gc29tZSBleHBlcmltZW50YXRpb24sIGEgJ0dyYXBlVHJlZScgbWluaW11bSBzcGFubmluZyBuZXR3b3JrIHdvcmtzIHdlbGwgZm9yIHZpc3VhbGlzaW5nIHRoZSBjbG9uYWxpdHkgb2YgdGhlc2UgcG9wdWxhdGlvbnMuIFdlIGNhbiB1c2UgYSBTTlAtc2NhbGVkIHBoeWxvZ2VueSBhcyBkaXJlY3QgaW5wdXQgdG8gR3JhcGVUcmVlLCBhbmQgdGhpcyB3aWxsIGFsbG93IGJyYW5jaGVzIHRvIGJlIHNjYWxlZCBhcHByb3ByaWF0ZWx5LiBIb3dldmVyLCBhbHRob3VnaCBhbm5vdGF0aW9uIGlzIGFsbG93ZWQgd2l0aGluIHRoZSBHcmFwZVRyZWUgc29mdHdhcmUsIGNvbG91cnMgbXVzdCBiZSBtYW51YWxseSBlZGl0ZWQuIEZpbmFsIEdyYXBlVHJlZSBwbG90cyBjYW4gdGhlbiBiZSBpbXBvcnRlZCBiYWNrIGludG8gUiBmb3IgY29tYmluaW5nIHdpdGggb3RoZXIgcGxvdHMuIApcCgpBbHRlcm5hdGl2ZSB2aXN1YWxpc2F0aW9ucyAtIGdyYXBldHJlZT8KXApUYWtlIHRoZSA1MjYtZ2xvYmFsIHBoeWxvZ2VueSAoc25wLXNjYWxlZCB2ZXJzaW9uIGZyb20gcHlqYXIpLCBhbmQgcHJ1bmUgdG8gb25seSBpbmNsdWRlIHRoZSBVSyBzdHJhaW5zIGZyb20gdGhpcyBzdHVkeSAtIHRoaXMgZW5zdXJlcyB0aGUgdG9wb2xvZ3kgaXMgY29uc2lzdGVudCBhY2Nyb3NzIHN0dWRpZXMuIApgYGB7cn0KClRQQS5weWphci50cmVlLnN1YnNldC51ayA8LSBhcGU6OmtlZXAudGlwKFRQQS5weWphci50cmVlLCBhcy5jaGFyYWN0ZXIodW5saXN0KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRHZW9fQ291bnRyeT09IlVLIiwiU2FtcGxlX05hbWUiXSkpKQoKClRQQS5weWphci50cmVlLnN1YnNldC5nbG9iYWxfYmVhc3Rfb25seS5zZXFsYW5lcyA8LSBUUEEubWV0YTIuMSAlPiUgZmlsdGVyKGZ1bGwudGVtcG9yYWwuYW5hbHlzaXM9PSdZZXMnKSAlPiUKICBzZWxlY3QoQ2xlYW5lZF9mYXN0cV9pZCkgJT4lIHB1bGwoKQoKVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLnNlcWxhbmVzIDwtIGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJEdlb19Db3VudHJ5PT0iVUsiLCJDbGVhbmVkX2Zhc3RxX2lkIl0pKQoKCmdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspCiN3cml0ZS50cmVlKFRQQS5weWphci50cmVlLnN1YnNldC51aywgcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEuVUstb25seS5weWphci4yMDIyLTAyLTAzLnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYApcClRyZWUgaW5kZXBlbmRlbnRseSB2aXN1YWxpc2VkIGFuZCBhbm5vdGF0ZWQgdXNpbmcgR3JhcGVUcmVlLgpcCk5vdyBpbXBvcnQgYW5kIGludGVncmF0ZSBHcmFwZVRyZWUgcGxvdCB3aXRoIG1ldGFkYXRhIHBsb3RzLgpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CiMgQ29tYmluZSB0aGUgcGxvdHMKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwuQjJGIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLm9yaWVudGF0aW9uLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5oaXYuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLkFnZS5jb21iaSwgbnJvdz0xLCByZWxfd2lkdGhzPWMoNyw0LDQsNCw0KSwgbGFiZWxzPWMoIkIiLCAiQyIsICJEIiwgIkUiLCAiRiIpLGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHZqdXN0PTEsIHNjYWxlPTAuOTcpCgojIGxlZ2VuZHMgYmVsb3cKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRiA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwuQjJGLCBQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoNywxKSkKCiNwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGCgoKIyBOb3cgYnJpbmcgaW4gZXh0ZXJuYWxseSBwbG90dGVkIEdyYXBldHJlZQpwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMuZmlsZSkKcC5UUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlIDwtIHBsb3RfZ3JpZChwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMsIHAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYsIG5jb2w9MSwgbGFiZWxzPWMoIkEiLCIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgcmVsX2hlaWdodHM9YygzLDUpKSAKCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiRmlnMV9UUEEtUEhFX1NhbXBsZS1kaXN0cm9zLXN1YmxpbmVhZ2UuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTkwLCBoZWlnaHQ9MTg1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTWFuYWdlIG90aGVyIEdyYXBlVHJlZSBwbG90cyAoZm9yIGNvbnNpc3RlbmN5KQoKVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMgpgYGB7cn0KIyBCcmluZyBpbiAzLXdheSBncmFwaGV0cmVlIHBsb3QgKDMgZGlmZmVyZW50IG1ldGFkYXRhIHZhcmlhYmxlcyB1c2luZyB0aGUgc2FtZSBpbnB1dCB0cmVlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnNF9UUEEtUEhFX0dyYXBldHJlZS0zd2F5cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xNDUsIGhlaWdodD0xODAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKClwKQW5kIGFsc28gZG8gdGhlIEhJViBzdGF0dXMgcGxvdApgYGB7cn0KClRQQS5VSy5HcmFwZXRyZWUuSElWIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZShUUEEuVUsuR3JhcGV0cmVlLkhJVi5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLkhJVgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWc1X1RQQS1QSEVfR3JhcGV0cmVlLUhJVi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0xMTAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKCgoKXApcCiMjIyBQaHlsb2dlbmV0aWMgY29udGV4dCBhbmFseXNlcwpcCk9rLCBub3cgbGV0cyBsb29rIGF0IHNvbWUgdHJlZXMKXApGaXJzdCwgbGV0J3MgZm9ybWFsaXNlIEJFQVNUIHRyZWUgcGxvdHRpbmcgYXMgdGhyZWUgc2VwYXJhdGUgZnVuY3Rpb25zIHRvIGVuYWJsZSBvdGhlciB0cmVlcyB0byBiZSBwbG90dGVkIHRoZSBzYW1lIHdheQpcCmBgYHtyfQpmdWxsLmJlYXN0Mi50cmVlIDwtIHJlYWQuYmVhc3QoZnVsbC5iZWFzdDIudHJlZS5maWxlKQpmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbCA8LSBnc3ViKCJcXHwuKyQiLCIiLGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8kdGlwLmxhYmVsLCBwZXJsPVQpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBmdW5jdGlvbiB0byBleHRyYWN0IGEgdHJlZSBiYXNlZCBvbiBzdWJsaW5lYWdlCkV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90IDwtIGZ1bmN0aW9uKG15LmJlYXN0LnRyZWUsIG15Lm1ldGFkYXRhLCBteS5waGUubWV0YSwgbXkuc3VibGluZWFnZSl7CiAgIyBnZXQgYWxsIHRpcHMgdG8gaW5jbHVkZSBmcm9tIG1ldGFkYXRhLCB0aGVuIGNhbGN1bGF0ZSBNUkNBIGZyb20gdHJlZQogIHN1YmxpbmVhZ2UudGVzdC5tcmNhIDwtIGdldE1SQ0EobXkuYmVhc3QudHJlZUBwaHlsbywgYXMuY2hhcmFjdGVyKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09bXkuc3VibGluZWFnZSwiU2FtcGxlX05hbWUiXSkpKQogICMjIyMjIwogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gdHJlZV9zdWJzZXQobXkuYmVhc3QudHJlZSwgbm9kZT1zdWJsaW5lYWdlLnRlc3QubXJjYSwgbGV2ZWxzX2JhY2s9MCkKICByZXR1cm4oVFBBLmJlYXN0LnN1YnRyZWUudGVzdCkKfQojRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGdW5jdGlvbiB0byBwcmVwYXJlIGEgYmVhc3QgdHJlZSB3aXRoIHRpbWVzY2FsZSBpbmRpY2F0b3JzLCBwb3N0ZXJpb3Igc3VwcG9ydCBhbmQgOTUlIEhQRCBiYXJzCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRCA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIG1yc2QuZnVsbHRyZWUpewogICMgZ2V0IE1SQ0QgZm9yIHRyZWUKICBtcnNkLkJlYXN0LnRyZWUudGVzdC5zIDwtIG1heChhcy5udW1lcmljKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRTYW1wbGVfTmFtZSAlaW4lIG15LmJlYXN0LnRyZWVAcGh5bG8kdGlwLmxhYmVsLCJTYW1wbGVfWWVhciJdKSkpCiAgbXJzZC5CZWFzdC50cmVlLnRlc3QgPC0gbHVicmlkYXRlOjp5bWQocGFzdGUwKG1yc2QuQmVhc3QudHJlZS50ZXN0LnMsIi0wNi0wMSIpKSAKICBtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUgPC0gbHVicmlkYXRlOjp5bWQobXJzZC5mdWxsdHJlZSkgCiAgI21yc2QuQmVhc3QudHJlZS50ZXN0CiAgIyBwbG90IGJhc2ljIHRyZWUKICBvcHRpb25zKGlnbm9yZS5uZWdhdGl2ZS5lZGdlPVRSVUUpCiAgcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0IDwtIGdndHJlZShteS5iZWFzdC50cmVlLCBtcnNkPW1yc2QuQmVhc3QudHJlZS50ZXN0LCBsYWRkZXJpemUgPSBULCBzaXplPTAuNCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxOTYwLDIwMjAsMTApLCBtaW5vcl9icmVha3M9c2VxKDIwMDAsIDIwMjAsIDEpKSArCiAgICB0aGVtZV90cmVlMigpICsKICAgICMgQWRkIGRhdGUgbGluZXMgZm9yIGVhc3kgaW50ZXJwcmV0YXRpb24gIAogICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciAgID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5NTAiLCBzaXplPS4yKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgICA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleTg1Iiwgc2l6ZT0uMiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpCiAgIyBBZGQgcG9zdGVyaW9yIHN1cHBvcnQgYXMgbm9kZSBwb2ludHMKICBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0ICsgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOCkpLGNvbG9yPSJncmF5NjAiLHNpemU9MixhbHBoYT0wLjUsIHNoYXBlPTE4KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOTEpKSxjb2xvcj0iZ3JheTQwIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPj0wLjk2KSksY29sb3I9ImJsYWNrIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KQogICMjIyMjIwogICMgZXh0cmFjdCA5NSUgSFBEIGludGVydmFscyAtIGdlb21fcmFuZ2Ugc2VlbXMgdW5hYmxlIHRvIGRvIGNvcnJlY3RseSB3aXRoIHRoaXMgdHJlZSAoa25vd24gYnVnIGZvciB0aXAgZGF0ZWQgdHJlZXMpLCBzbyBleHRyYWN0IGRhdGEgYW5kIHBsb3QgdXNpbmcgZ2VvbV9zZWdtZW50CiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhIDwtIGZvcnRpZnkobXkuYmVhc3QudHJlZSkKICBtaW5tYXggPC0gdChtYXRyaXgodW5saXN0KFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YVshaXMubmEoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhJGhlaWdodF8wLjk1X0hQRCksImhlaWdodF8wLjk1X0hQRCJdKSxucm93PTIpKQogIGJhcl9kZiA8LSBkYXRhLmZyYW1lKG5vZGVfaWQ9VFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhWyFpcy5uYShUUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGEkaGVpZ2h0XzAuOTVfSFBEKSwibm9kZSJdLGFzLmRhdGEuZnJhbWUobWlubWF4KSkKICBuYW1lcyhiYXJfZGYpIDwtIGMoJ25vZGVfaWQnLCdtaW4nLCdtYXgnKSAKICBiYXJfZGYgPC0gYmFyX2RmICU+JSBmaWx0ZXIobm9kZV9pZCA+IE50aXAobXkuYmVhc3QudHJlZUBwaHlsbykpCiAgYmFyX2RmIDwtIGJhcl9kZiAlPiUgbGVmdF9qb2luKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YSwgYnk9Yygnbm9kZV9pZCc9J25vZGUnKSkgIyU+JSBzZWxlY3Qobm9kZV9pZCxtaW4sbWF4LHkpCiAgI21yY2QuZGVjaW1hbCA8LSBkZWNpbWFsX2RhdGUobXJzZC5CZWFzdC50cmVlLnRlc3QpCiAgbXJjZC5kZWNpbWFsIDwtIGRlY2ltYWxfZGF0ZShtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUpCiAgCiAgIyBOb3cgYWRkIEhQRHMgdG8gcGxvdAogIHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCA8LSBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgKyBnZW9tX3NlZ21lbnQoYWVzKHg9bXJjZC5kZWNpbWFsLW1heCwgeT15LCB4ZW5kPW1yY2QuZGVjaW1hbC1taW4sIHllbmQ9eSksIGRhdGE9YmFyX2RmLCBjb2xvcj0ncmVkJywgYWxwaGE9MC4yLCBzaXplPTIuMCkKICAjIE91dHB1dCB0cmVlIAogIHJldHVybihwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QpCn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRnVuY3Rpb24gdG8gYWRkIG1ldGFkYXRhIHRvIHRyZWUKIyBIYXMgdHdvIG9wdGlvbmFsIGFyZ3VtZW50cyAiaW5pdGlhbC50cmFjay5vZmZzZXQiIGFuZCAidHJhY2suc2NhbGluZyIgd2hpY2ggY2FuIGJlIHVzZWQgdG8gYWx0ZXIgdGhlIHdpZHRoIGFuZCBwb3NpdGlvbmluZyBvZiBtZXRhZGF0YSB0cmFja3MKCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YSA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLmlucHV0LCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIGluaXRpYWwudHJhY2sub2Zmc2V0LCB0cmFjay5zY2FsaW5nKXsKICAgICMgQWRkIGNvZGUgdG8gYWxsb3cgc2NhbGluZyB1cCBvZiB0aGUgdHJhY2sgb2Zmc2V0cyBhbmQgd2lkdGhzIC0gdXNlZnVsIGZvciBtdWNoIGJpZ2dlciBsZW5ndGggdHJlZXMKICBpZihtaXNzaW5nKGluaXRpYWwudHJhY2sub2Zmc2V0KSl7CiAgICBpbml0aWFsLnRyYWNrLm9mZnNldCA8LSAwCiAgfSAgICAKICBpZihtaXNzaW5nKHRyYWNrLnNjYWxpbmcpKXsKICAgIHRyYWNrLnNjYWxpbmcgPC0gMQogIH0KICAjIENhbGN1bGF0ZSBhbW91bnQgdG8gb2Zmc2V0IGVhY2ggaGVhdG1hcCB0cmFjawogIG9mZnNldC5kaXN0IDwtIDQqdHJhY2suc2NhbGluZwogIHRyYWNrLndpZHRoIDwtICgxL21heChteS5iZWFzdC50cmVlLmlucHV0JGRhdGEkaGVpZ2h0KSozKSp0cmFjay5zY2FsaW5nCiAgCiAgIyBtYWtlIGEgbGlzdCBvZiB0YXhhIHVzZWQgaW4gdGhpcyBwbG90IAogIG15LnRheGEubGlzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KGZpbHRlcihteS5iZWFzdC50cmVlLmlucHV0JGRhdGEsIGlzVGlwPT1UUlVFKSAlPiUgc2VsZWN0KGxhYmVsKSkpCiAgCiAgIyBtYWtlIGEgY29sb3Igc2NhbGUgZm9yIHNhbXBsaW5nIHllYXJzCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9c29ydCh1bmlxdWUoYXMubnVtZXJpYyh1bmxpc3QobXkubWV0YWRhdGFbKG15Lm1ldGFkYXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0KSwiU2FtcGxlX1llYXIiXSx1c2UubmFtZXM9RikpKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDcsICJZbE9yUmQiKSkobnJvdyhQSEUuc3VibGludGVzdC55ZWFyLmNvbHMpKQogIAogICMgT3IgYWx0ZXJuYXRpdmVseSwgdXNlIGEgY29tbW9uIGNvbG91ciBzY2hlbWUgZm9yIGFsbCBkYXRhIChtYXliZSBtb3JlIHNlbnNpYmxlKQogIFBIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9VFBBLnllYXIuY3V0dG9mZi5jb2xzJGRhdGUuY3V0dG9mZiwgeWVhci5jb2xzPVRQQS55ZWFyLmN1dHRvZmYuY29scyRkYXRlLmN1dHRvZmYuY29sLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIG1ha2UgbWV0YWRhdGEgZmlsZSBmb3IgVUsgcmVnaW9ucyBwcmVzZW50IGluIHN1YmxpbmVhZ2UKICBzdWJsaW4udGVzdC5yZWdpb24ubWV0YSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJTYW1wbGVfTmFtZSJdKSksIFJlZ2lvbj1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJwaGVfY2VudHJlIl0pKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgCiAgIyBBZGQgaGVhdG1hcCBzdHJpcHMKICAjIFNhbXBsZSBZZWFyCiAgI1RQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKG15LmJlYXN0LnRyZWUuaW5wdXQsIFRQQS5yYXdzZXEuYWxsLlllYXJzLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCtvZmZzZXQuZGlzdCxjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsKICAgICNzY2FsZV9maWxsX21hbnVhbChuYW1lPSJZZWFyIiwgdmFsdWVzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMsYnJlYWtzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsIG5jb2w9MikpICsKICAgICNnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAobXkuYmVhc3QudHJlZS5pbnB1dCwgVFBBLnJhd3NlcS55ZWFyLmN1dHRvZmYucCwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCwgb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0K29mZnNldC5kaXN0LGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iWWVhciIsIHZhbHVlcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhci5jb2xzLGJyZWFrcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhciwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxLCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgCiAgIyBBZGQgY291bnRyeQogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsLCBUUEEucmF3c2VxLmNvdW50cmllcy5wLCBjb2xvcj1OVUxMLHdpZHRoPXRyYWNrLndpZHRoLCBvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrKG9mZnNldC5kaXN0KjIpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkNvdW50cnkiLCB2YWx1ZXM9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJGNvdW50cnkuY29sLCBicmVha3M9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJEdlb19Db3VudHJ5LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgIyBVSyBvciBub24tVUsKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUEEucmF3c2VxLlVLLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCozKSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJFbmdsYW5kL090aGVyIiwgYnJlYWtzPWMoIkVuZ2xhbmQiLCJPdGhlciIpLCB2YWx1ZXM9YygiYmxhY2siLCJncmV5OTUiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDMsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICAjIFVLIFBIRSByZWdpb24KICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwgc3VibGluLnRlc3QucmVnaW9uLm1ldGEsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCsob2Zmc2V0LmRpc3QqNCksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtIU0EgUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24sIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA0KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQogIAogICMgVFBBIHN1YmxpbmVhZ2UKICAjVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRiksIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCo1KSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPTIuNSkgKyAKICAjc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgCiAgCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgICBnZW9tX3Jvb3RlZGdlKDIpICsKICAgIE5VTEwKICAKICAjIGNhbGN1bGF0ZSBudW1iZXIgb2YgdGF4YQogIHRlc3QudGF4YWNvdW50IDwtIGxlbmd0aChteS50YXhhLmxpc3QpCiAgIyBBZGp1c3QgZmluYWwgcGxvdCB4IGFuZCB5IGF4aXMgdG8gbWFrZSBzcGFjZSBmb3IgbGFiZWxzIHVzaW5nIHRheGEgY291bnRzCiAgeC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih5PWMoLTAuNS0odGVzdC50YXhhY291bnQvMTUpLHRlc3QudGF4YWNvdW50KzIpLCB4PWMoeC5heGlzLmxpbWl0c1sxXSx4LmF4aXMubGltaXRzWzJdKzMpKQogIAogIHJldHVybihUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCkKfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmBgYAoKXApHcmVhdCwgbm93IGxldCdzIHBsb3QgYSBmdWxsIGJlYXN0IHRyZWUKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KIyBmdW5jdGlvbiBmb3IgeC1heGlzIHRpbWUgYnJlYWtzIG5lZWRzIHR3ZWFraW5nIGZvciB0aGUgZnVsbCB0cmVlClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQobXkuYmVhc3QudHJlZSA9IGZ1bGwuYmVhc3QyLnRyZWUsIG15Lm1ldGFkYXRhID0gVFBBLm1ldGEyLjEsIG15LnBoZS5tZXRhID0gUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXJzZC5mdWxsdHJlZSA9ICIyMDE5LTA2LTAxIikgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxNDAwLDIwMjAsNTApLCBtaW5vcl9icmVha3M9c2VxKDE5NTAsIDIwMjAsIDUpKSwgbXkubWV0YWRhdGEgPSBUUEEubWV0YTIuMSwgbXkucGhlLm1ldGEgPSBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gNSkKClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnN19UUEFfRnVsbEJlYXN0VHJlZS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0yNDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKXApOb3cgZG8gc3VibGluZWFnZSBwbG90cwpcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyBTdWJsaW5lYWdlIDEKc3VibGluZWFnZS4xLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMSksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMS4yKQoKIyBTdWJsaW5lYWdlLjIKc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMSkKCiMgU3VibGluZWFnZS44CnN1YmxpbmVhZ2UuOC50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDgpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCiMgU3VibGluZWFnZS4xNApzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCmBgYAoKXApQbG90IHRvZ2V0aGVyPwpcCk1heWJlIHdpdGggc3VibGluZWFnZSAxIGV4cGFuZGVkPwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpwLmJlYXN0LnRyZWVzLmhlYXRtYXAuc3VibGluZWFnZXMuY29tYmkub2Zmc2V0MSA8LSBwbG90X2dyaWQoc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCwgCiAgICAgICAgICBzdWJsaW5lYWdlLjgudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIG5jb2w9MiwgbGFiZWxzPWMoIkIgLSBTdWJsaW5lYWdlIDIiLCJDIC0gU3VibGluZWFnZSA4IiwiRCAtIFN1YmxpbmVhZ2UgMTQiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSwgdmp1c3Q9MS4wKQoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIgPC0gcGxvdF9ncmlkKHN1YmxpbmVhZ2UuMS50cmVlLmhlYXRtYXAsIHAuYmVhc3QudHJlZXMuaGVhdG1hcC5zdWJsaW5lYWdlcy5jb21iaS5vZmZzZXQxLCBsYWJlbHM9YygiQSAtIFN1YmxpbmVhZ2UgMSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NzUsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDYsMTEpLCB2anVzdD0yLjUpCgoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzhfVFBBLVBIRV9TdWJsaW5lYWdlLUJlYXN0VHJlZXMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjY1LCBoZWlnaHQ9MjMwLCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTmVlZCB0byBleHBsb3JlIHN1YmxpbmVhZ2UgMTQgYSBiaXQgbW9yZSB0byBnZXQgZGF0ZXMgZm9yIHRob3NlIHN1YmNsYWRlcwpgYGB7cn0Kc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGxpbmVzaXplPTAuNCkgIzMKYGBgCgpcCmBgYHtyfQojIE9rLCB0aGVyZSBhcmUgbXVsdGlwbGUgc3ViY2xhZGVzIGluIHRoaXMgdHJlZQpzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwJGRhdGEKCiMgZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLGMoIlBIRTE1MDE1MEEiLCJOTDE0IiwiVFBBX0JDQzEyMiIsIlRQQV9CQ0MxMjYiLCJQSEUxNDAwNzZBIiwiVFBBX1VLQlJHMDA4IikpICA5ODIKIyBmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbFtwaGFuZ29ybjo6RGVzY2VuZGFudHMoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgOTgyLCB0eXBlID0gYygidGlwcyIpKVtbMV1dXQoKc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QgPC0gYygiTkwxNyIsICJOTDE5IiwgIlBIRTE0MDA4NUEiLCAiUEhFMTQwMDg5QSIsICJQSEUxNTAxMThBIiwgIlBIRTE1MDEyMUEiLCAiUEhFMTUwMTMzQSIsICJQSEUxNTAxNDNBIiwgIlBIRTE1MDE0NUEiLCAiUEhFMTUwMTYyQSIsICJQSEUxNTAxNjZBIiwgIlBIRTE1MDE2OEEiLCAiUEhFMTYwMjI0QSIsICJQSEUxNjAyNDNBIiwgIlBIRTE2MDI1NUEiLCAiUEhFMTYwMjc2QSIsICJQSEUxNjAyOTBBIiwgIlBIRTE2MDMwMkEiLCAiUEhFMTYwMzA2QSIsICJQSEUxNzAzMzNBIiwgIlBIRTE3MDM0OUEiLCAiUEhFMTcwMzc0QSIsICJQSEUxNzAzODFBIiwgIlBIRTE3MDY2NEEiLCAiVFBBX0VTQkNOMDA1IiwgIlRQQV9VS0JJUjAzMiIpCgpzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCA8LSBjKCJOTDE0IiwgIlBIRTE0MDA3NkEiLCAiUEhFMTUwMTQ5QSIsICJQSEUxNTAxNTBBIiwgIlBIRTE1MDE3MEEiLCAiUEhFMTYwMTk2QSIsICJQSEUxNjAyNjNBIiwgIlBIRTE2MDI3NEEiLCAiUEhFMTYwMjg3QSIsICJQSEUxNjAyOTRBIiwgIlBIRTE2MDMxNkEiLCAiUEhFMTYwMzE3QSIsICJQSEUxNzAzNzJBIiwgIlBIRTE3MDM4NkEiLCAiUEhFMTcwMzk3QSIsICJQSEUxNzA0MDVBIiwgIlRQQV9CQ0MwODEiLCAiVFBBX0JDQzA4OCIsICJUUEFfQkNDMDg5IiwgIlRQQV9CQ0MxMDEiLCAiVFBBX0JDQzEyMiIsICJUUEFfQkNDMTI2IiwgIlRQQV9CQ0MxMzYiLCAiVFBBX0JDQzE2OSIsICJUUEFfSFVOMTgwMDA0IiwgIlRQQV9IVU4xOTAwMjAiLCAiVFBBX1VLQklSMDQ0IiwgIlRQQV9VS0JSRzAwNyIsICJUUEFfVUtCUkcwMDgiKQoKIyBHZXQgTVJDQSBkYXRlIGZvciBsb3dlciBjbGFkZQpzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdC50bXJjYSA8LSBzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhW3N1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDE0KUBwaHlsbywgc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QpLCJ4Il0KCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgMTQgbG93ZXIgY2xhZGU6ICIsc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QudG1yY2EpCgojIEdldCBNUkNBIGRhdGUgZm9yIHVwcGVyIGNsYWRlCnN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0LnRtcmNhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGFbc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAuZGF0YSRub2RlPT1nZXRNUkNBKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpQHBoeWxvLCBzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCksIngiXQoKcGFzdGUwKCJUTVJDQSBmb3Igc3VibGluZWFnZSAxNCB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdC50bXJjYSkKYGBgClwKXApFeHRyYWN0IGtleSBpbmZvcm1hdGlvbiBmb3Igc3VibGluZWFnZSA2ICh0d28gc2FtcGxlcykKYGBge3J9CnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCkKCnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YSA8LSBzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwJGRhdGEKCiMgR2V0IE1SQ0EgZGF0ZSBmb3IgdXBwZXIgY2xhZGUKc3VibGluZWFnZS42LmJlYXN0dHJlZS50bXJjYSA8LSBhcy5udW1lcmljKHN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YVtzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpQHBoeWxvLCBjKCJQSEUxMzAwNDhBIiwgIlBIRTE2MDI4M0EiKSksImJyYW5jaCJdKQoKCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgNiB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjYuYmVhc3R0cmVlLnRtcmNhKQpgYGAKCgoKXApcCiMjIyBFeHRyYWN0IHNhbXBsZSAmIHBvcHVsYXRpb24gc3RhdGlzdGljcyBmcm9tIGRhdGFzZXRzIGZvciB1c2UgaW4gbWFudXNjcmlwdCB0ZXh0ClwKRGF0YXNldCBhbmQgR2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbnMKYGBge3J9CiMgZGF0YXNldCBjb3VudHMKcGFzdGUwKCJUb3RhbCBVSyBzYW1wbGVzIGluIGNsZWFuZWQvZGVkdXBsaWNhdGVkIGRhdGFzZXQ6ICIsbnJvdyhQSEUubWV0YWRhdGEubGlua2VkKSkKcGFzdGUwKCJPZiB3aGljaDogIixucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5QSEU9PSJQSEUiLF0pLCIgZnJvbSBQSEUgUmVmIGxhYiBhdCBDb2xpbmRhbGUiKQpwYXN0ZTAoIk9mIHdoaWNoOiAiLG5yb3coUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJGlzLlBIRT09Ik90aGVyIixdKSwiIGZyb20gb3RoZXIgbGFicyIpCgojIHByb3BvcnRpb24gd2l0aCBnZW9ncmFwaGljYWwgZGF0YQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkWyhQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgJW5vdGluJSBjKCJOb3QgS25vd24iLCJVSyAobm90IEVuZ2xhbmQpIikpLF0pLCIgd2VyZSBncm91cGVkIGludG8gb25lIG9mIHRoZSA5IFBIIHJlZ2lvbnMiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09IlVLIChub3QgRW5nbGFuZCkiLF0pLCAiIHdlcmUgcmVmZXJyZWQgZnJvbSBvdXRzaWRlIEVuZ2xhbmQiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsXSksICIgaGFkIHVua25vd24gcmVnaW9uIikKCiMgY291bnRzICYgZnJhY3Rpb25zIGJ5IFBIRSByZWdpb24KUEhFLmdlby5jb3VudAoKYGBgClwKR2VuZGVyIE9yaWVudGF0aW9uIHN0YXRzCmBgYHtyfQpQSEUub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8uSElWLmNvdW50cwpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMKUEhFLnN1YmxpbmVhZ2UuQWdlCmBgYAoKXApTdWJsaW5lYWdlIERpc3RyaWJ1dGlvbnMKYGBge3J9ClBIRS5MaW5lYWdlLmNvdW50ClBIRS5zdWJsaW4uY291bnQKUEhFLmdlby5zdWJsaW5lYWdlCmBgYAoKXApNYWNyb2xpZGUgcmVzaXN0YW5jZSBzdGF0cwpgYGB7cn0KVUsubWFjcm9saWRlLnJlcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JQogIGRwbHlyOjpncm91cF9ieShBMjA1OEcsIEEyMDU5RykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudC5hbGxlbGU9bigpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKENvdW50LmFsbGVsZSksIHBlcmMuYWxsZWxlPXJvdW5kKChDb3VudC5hbGxlbGUvdG90YWwuY291bnQpKjEwMCwxKSkKVUsubWFjcm9saWRlLnJlcwoKVUsubWFjcm9saWRlLnJlcy5zdWJsaW4gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIEEyMDU4RywgQTIwNTlHKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmFsbGVsZT1uKCkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLmNvdW50PXN1bShDb3VudC5hbGxlbGUpLCBwZXJjLmFsbGVsZT1yb3VuZCgoQ291bnQuYWxsZWxlL3RvdGFsLmNvdW50KSoxMDAsMSkpClVLLm1hY3JvbGlkZS5yZXMuc3VibGluCgoKIyBDYWxjdWxhdGUgbG9uZyBmb3JtIGRmLCB3aXRoIGRpZmZlcmVudCAyM1MgYWxsZWxlcyAoQTIwNThHLCBBMjA1OUcsIFdULCBVbmNlcnRhaW4pIHYucy4gc3VibGluZWFnZQpVSy5tYWNyb2xpZGUucmVzLnN1Ymxpbi5sb25nIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgbXV0YXRlKFJlc2lzdGFuY2UuYWxsZWxlPWlmZWxzZShBMjA1OEc9PSJZZXMiLCAiQTIwNThHIiwgaWZlbHNlKEEyMDU5Rz09IlllcyIsICJBMjA1OUciLCBpZmVsc2UoKEEyMDU4Rz09Ik5vIiAmIEEyMDU5Rz09Ik5vIiksIldpbGQgVHlwZSIsICJVbmNlcnRhaW4iKSkpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIFJlc2lzdGFuY2UuYWxsZWxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcz1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMpLCAKICAgICAgICAgICAgICAgIGZyYWN0aW9uPUNvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcy90b3RhbC5zdWJsaW4pICU+JQogICNkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjphcnJhbmdlKChSZXNpc3RhbmNlLmFsbGVsZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoUmVzaXN0YW5jZS5hbGxlbGUgPSBmYWN0b3IoUmVzaXN0YW5jZS5hbGxlbGUsIGxldmVscz1yZXYoYygiQTIwNThHIiwgIkEyMDU5RyIsICJVbmNlcnRhaW4iLCAiV2lsZCBUeXBlIikpKSkKCiMgTWFrZSBwbG90IG9mIG1hY3JvbGlkZSByZXNpc3RhbmNlIGJ5IHN1YmxpbmVhZ2VzCnAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QgPC0gZ2dwbG90KFVLLm1hY3JvbGlkZS5yZXMuc3VibGluLmxvbmcsIGFlcyhDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGZpbGw9UmVzaXN0YW5jZS5hbGxlbGUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTWFjcm9saWRlXG5SZXNpc3RhbmNlXG5BbGxlbGUiLHZhbHVlcz1jKCJpbmRpYW5yZWQyIiwgInN0ZWVsYmx1ZTEiLCJncmV5NTUiLCAiZ3JleTkwIiksIGJyZWFrcz1jKCJBMjA1OEciLCAiQTIwNTlHIiwgIlVuY2VydGFpbiIsICJXaWxkIFR5cGUiKSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJQcm9wb3J0aW9uIHdpdGggTWFjcm9saWRlIFJlc2lzdGFuY2UgQWxsZWxlIikgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKSArCiAgZ2VvbV90ZXh0KGRhdGE9VUsubWFjcm9saWRlLnJlcy5zdWJsaW4ubG9uZywgYWVzKGN1bV9mcmFjdC5taWQsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQucGVyLnN1Ymxpbi5NYWNyb2xpZGVzKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90CgoKIyBDb21iaW5lIHBsb3Qgd2l0aCBzdWJsaW5lYWdlIGNvdW50IGJhcnMKcC5zdWJsaW4uTWFjcm9saWRlcy5oYmFycGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpLCBwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj1ULCBsYWJlbHM9YygiQSIsICJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90LmNvbWJpCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnOV9UUEEtUEhFX1N1Ymxpbi1NYWNyb2xpZGUtUmVzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE2MCwgaGVpZ2h0PTEyMCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYApcClwKCiMjIyBQYWlyd2lzZSBTTlAgYW5hbHlzaXMKXApPSywgd2FudCB0byBpbnZlc3RpZ2F0ZSB0aGUgZGlmZmVyZW50IHBhdHRlcm5zIG9ic2VydmFibGUgZm9yIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQgKHBhbGUgYmx1ZSkgaW4gU3VibGluZWFnZSAxClwKTXVsdGlwbGUgd2F5cyB3ZSBjYW4gZG8gdGhpcyAtIGluY2x1ZGluZyBTTlAgZGlzdGFuY2VzIChhbHNvIG11bHRpcGxlIHdheXMgdG8gZG8gdGhhdCkKXApgYGB7cn0KIyMjCiNVc2UgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGZyb20gdGhlIFNOUCBzY2FsZWQgdHJlZQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdCA8LSBhcGU6OmNvcGhlbmV0aWMucGh5bG8oVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrKQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0IDwtIGRhdGEuZnJhbWUoVGF4YTE9cm93Lm5hbWVzKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0KSwgVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAlPiUgdGlkeXI6OmdhdGhlcihUYXhhMiwgRGlzdGFuY2UuUGh5bG8sIC1UYXhhMSkKIyBUYXhhIENvbXBhcmlzb25zIGxhYmVsClRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YV9jb21iaW5hdGlvbiA8LSBzYXBwbHkoMTpucm93KFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQpLCBmdW5jdGlvbiAoeCkgcGFzdGUwKHNvcnQoYyhhcy5jaGFyYWN0ZXIoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCRUYXhhMVt4XSksYXMuY2hhcmFjdGVyKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YTJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQojIE1lcmdlIHRvZ2V0aGVyCiNUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSBkcGx5cjo6bGVmdF9qb2luKFRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0LCBUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0WyxjKCJUYXhhX2NvbWJpbmF0aW9uIiwiRGlzdGFuY2UuUGh5bG8iKV0sIGJ5PSJUYXhhX2NvbWJpbmF0aW9uIikKClRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0IDwtIFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQKCgpUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSB1bmlxdWUoVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQpCmBgYApcCk9rLCBub3cgYnJpbmcgaW4gc29tZSBtZXRhZGF0YSBhbmQgY29tcGFyaXNvbnMKYGBge3J9CiMgQnJpbmcgaW4gYW5kIG1lcmdlIG1ldGFkYXRhClBIRS5tZXRhLnBhaXJ3aXNlLnQxIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwieWVhciIsInBoZV9jZW50cmUiLCJsb25kb24iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsICJUUEFfTGluZWFnZSIsIkdlb19Db3VudHJ5IiwiaXMuVUsiLCJpcy5QSEUiLCAiU2FtcGxlX1llYXIiLCJkYXRlLmRlY2ltYWwiKV0KCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQxKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDEpLCIudDEiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MSlbMV0gPC0gIlRheGExIgpQSEUubWV0YS5wYWlyd2lzZS50MiA8LSBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInllYXIiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCAiVFBBX0xpbmVhZ2UiLCJHZW9fQ291bnRyeSIsImlzLlVLIiwiaXMuUEhFIiwgIlNhbXBsZV9ZZWFyIiwiZGF0ZS5kZWNpbWFsIildCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQyKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDIpLCIudDIiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MilbMV0gPC0gIlRheGEyIgoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQsUEhFLm1ldGEucGFpcndpc2UudDEsIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKSAKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLFBIRS5tZXRhLnBhaXJ3aXNlLnQyLCBieT0iVGF4YTIiLCB0eXBlPSJsZWZ0IikKCiMgRXhjbHVkZSBtaXNzaW5nIGRhdGEgKGUuZy4gbWlzc2luZyBzdWJsaW5lYWdlKSAtIHRoaXMgd2lsbCBhbHNvIHJlbW92ZSBub24tVUsgc2FtcGxlcywgc2luY2UgZnVsbCBtZXRhZGF0YSBpcyBtaXNzaW5nIGhlcmUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQxKSxdClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MiksXQoKYGBgCgpcCkRlZmluZSBjb21wYXJpc29ucwpgYGB7cn0KIyBTYW1lIHNhbXBsZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGUgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUYXhhMT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGEyLCJzYW1lIiwgImRpZmZlcmVudCIpCgojIFllYXJzIGJldHdlZW4gc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci50MSkgLSBhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSR5ZWFyLnQyKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRTYW1wbGVfWWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDIpKQoKIyBZZWFycyBiZXR3ZWVuIGRlY2ltYWwgZGF0ZSAobW9yZSBwcmVjaXNlIHRlbXBvcmFsIGRpc3RhbmNlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlIDwtIGFicyhhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkYXRlLmRlY2ltYWwudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGF0ZS5kZWNpbWFsLnQyKSkKCiMgRXBpZGVtaW9sb2dpY2FsIHRpbWUgYmV0d2VlbiAtIGNhdGFnb3JpY2FsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8MS8xMiwibW9udGgiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9My8xMiwgInF1YXJ0ZXIiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9Ni8xMiwgImhhbGYgeWVhciIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xLCAiMSB5ZWFyIixpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MiwgIjIgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MywgIjMgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NCwgIjQgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NSwgIjUgeWVhcnMiLCAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTYsICI2IHllYXJzIiwiPjYgeWVhcnMiKSkpKSkpKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0IDwtIGZhY3RvcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBsZXZlbHM9YygibW9udGgiLCAicXVhcnRlciIsImhhbGYgeWVhciIsIjEgeWVhciIsICIyIHllYXJzIiwgIjMgeWVhcnMiLCAiNCB5ZWFycyIsICI1IHllYXJzIiwgIjYgeWVhcnMiLCAiPjYgeWVhcnMiKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQueWVhcnMgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTEsICIwIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTIsICIxIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTMsICIyIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTQsICIzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTUsICI0IiwgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD02LCAiNSIsIj41IikpKSkpKQoKCiMgU2FtZSBjb3VudHJ5ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLmNvdW50cnkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MSA9PSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkR2VvX0NvdW50cnkudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIElzIFVLClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuVUsudDEgPT0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGlzLlVLLnQyLCAic2FtZSIsICJkaWZmZXJlbnQiKQoKIyBJcyBQSEUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGguUEhFIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuUEhFLnQxID09IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpcy5QSEUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIFNhbWUgVFBBIExpbmVhZ2UgKGNsZWFuZWQgdXAgY2xhc3NpZmljYXRpb25zKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQV9MaW5lYWdlLnQxPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDFbeF09PSIwIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEFfTGluZWFnZS50Mlt4XT09IjAiKSxOQSxQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZVt4XSkpCgojIFNhbWUgVFBBIHN1YmxpbmVhZ2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXIgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQyLCJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKCgoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MVt4XT09IlNpbmdsZXRvbiIpIHwoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50Mlt4XT09IlNpbmdsZXRvbiIpKSwiZGlmZmVyZW50IixQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuUGluZWNvbmUuY2x1c3Rlclt4XSkpCgojIERlZmluZSBHZW5ldGljIHJlbGF0aW9uc2hpcHMgaGllcmFyY2hpY2FsbHkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbm9taWMuY2x1c3Rlci5oaWVyYXJjaHkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSREaXN0YW5jZT09MCwiWmVyb19TTlBzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyPT0ic2FtZSIsIlNhbWUgU3VibGluZWFnZSIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZT09InNhbWUiLCAiU2FtZSBMaW5lYWdlIiwiRGlmZmVyZW50IExpbmVhZ2UiKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2Vub21pYy5jbHVzdGVyLmhpZXJhcmNoeS5waCA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLCJaZXJvX1NOUHMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXI9PSJzYW1lIiwiU2FtZSBTdWJsaW5lYWdlIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlPT0ic2FtZSIsICJTYW1lIExpbmVhZ2UiLCJEaWZmZXJlbnQgTGluZWFnZSIpKSkKCgojIFNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5QSEUucmVnaW9uIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUuY2VudHJlLmNvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxW3hdKSxhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQoKIyBkb2VzIHRoZSBjb21iaW5hdGlvbiBpbmNsdWRlZCBMb25kb24/ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpbnZvbHZlcy5Mb25kb24gPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxPT0iTG9uZG9uIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQyPT0iTG9uZG9uIiwgIkxvbmRvbiIsICJub3QtTG9uZG9uIikKCgojIE9yaWVudGF0aW9uIHBhaXIKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJE9yaWVudGF0aW9uX2NvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0pLGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdKSkpLGNvbGxhcHNlPSJfX18iKSkKCiNQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkT3JpZW50YXRpb24uQ2xhc3MgPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbiAoeCkgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TTSIsICJNU00iLAojICAgICAgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQxW3hdPT0iTVNNIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJNU00iLCAiTWl4ZWQiLCAKIyAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU1ciICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09IldTTSIsIkhldGVyb3NleHVhbCIsIAojICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJXU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TVyIsIkhldGVyb3NleHVhbCIsIlVua25vd24iKSkpKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRPcmllbnRhdGlvbi5DbGFzcyA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSksIGZ1bmN0aW9uICh4KSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IkdCTVNNIiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJHQk1TTSIsICJHQk1TTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF0gJWluJSBjKCJNU1ciLCJXU00iKSwiSGV0ZXJvc2V4dWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdICVpbiUgYygiTVNXIiwiV1NNIiksICJNaXhlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIsICJNaXhlZCIsICJVbmtub3duIikpKSkpCiAgICAgICAgICAgICAgICAgICAgCgoKIyBDb3VudHJ5IENvbXBhcmlzb25zIGxhYmVsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRDb3VudHJ5X2NvbWJpbmF0aW9ucyA8LSBwYXN0ZTAoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJEdlb19Db3VudHJ5LnQxLCJfX18iLFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MikKCiMgU3Vic2V0IHRvIFBIRSBkYXRhIG9ubHkgKGVmZmVjdGl2ZWx5IGFscmVhZHkgZG9uZSwgYnV0IGxldCdzIGJlIGV4cGxpY2l0KQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVsoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGgudWs9PSJzYW1lIiAmICBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkYm90aC5QSEU9PSJzYW1lIiksXQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUub25seT09IlBIRSIsXQoKUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrPT0ic2FtZSIpLF0KCmBgYApcClwKCmBgYHtyfQojIE1ha2Ugc2luZ2xlIHNpZGVkClBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshZHVwbGljYXRlZChQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGFfY29tYmluYXRpb24pLF0KCmBgYAoKClwKXAojIyMgUGVyZm9ybSBhIG1vcmUgZGV0YWlsZWQgYW5hbHlzaXMgb2Ygc2FtcGxlcyBmcm9tIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQKXApEbyBhIG1vcmUgZGV0YWlsZWQgZXhwbG9yYXRpb24gb2YgdGhlIE5vcnRoIEVhc3Qgb2YgRW5nbGFuZApcCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00fQpQSEUubWV0YWRhdGEubGlua2VkMi5yZWdpb25fTm9ydGhFYXN0IDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlPT0iTm9ydGggRWFzdCIsXQoKIyBDb25zdHJhaW4gYnkgc2FtcGxlcyBiZWluZyBmcm9tIHRoZSBOb3J0aCBFYXN0ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09Ik5vcnRoIEVhc3QiICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiksXQoKIyBDb25zdHJhaW4gYnkgdGhlIHNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnNbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyRzYW1lLlBIRS5yZWdpb249PSJzYW1lIixdCgojSnVzdCBwbG90IHRoZXNlIGRpc3Ryb3MKcC5Ob3J0aEVhc3QuUGFpcndpc2UuU05Qcy51bmNvbnN0cmFpbmVkIDwtIGdncGxvdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLCBhZXMoRGlzdGFuY2UuUGh5bG8pKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgbGFicyh4PSJQYWlyd2lzZSBTTlAgRGlzdGFuY2UiLCB5PSJDb21wYXJpc29uIENvdW50IikKCnAuTm9ydGhFYXN0LlBhaXJ3aXNlLlNOUHMudW5jb25zdHJhaW5lZApgYGAKClwKTWFrZSBhIHNpbmdsZSBsaW5rYWdlIG5ldHdvcmsgZnJvbSB0aGUgTm9ydGggRWFzdCBzYW1wbGVzCmBgYHtyfQoKIyBDb25zdHJhaW4gYnkgU05QIGRpc3RhbmNlIChsb29zZXIgdGhhbiBwcmV2aW91c2x5IC0gd2UganVzdCB3YW50IHRvIGZpbmQgYmFzaWMgZ3JvdXBpbmdzIHdpdGhpbiBzdWJsaW5lYWdlIDEgZm9yIE5FIHNhbXBsZXMpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvPD0yLF0KCiMgQW5kIG1ha2Ugc3VyZSB0aGF0IHdlIGFjdHVhbGx5IGhhdmUgZ2VuZXRpYyBkaXN0YW5jZSBkYXRhIGZvciBhbGwgc2FtcGxlcyB3aXRoaW4gdGhlIG5ldHdvcmsKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvKSxdCgojIGNsZWFudXAgc29tZSBkYXRhIG5vaXNlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyR5ZWFyLnQxKSxdCgojIHByZXBhcmUgaW50cHV0IGRhdGEgKHdpdGggZWRnZSBpbmZvKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyxjKCJUYXhhMSIsIlRheGEyIiwiRGlzdGFuY2UuUGh5bG8iLCJkZWNpbWFsLmRhdGUuZGlzdGFuY2UiLCJ5ZWFyLmRpc3RhbmNlIiwiT3JpZW50YXRpb24uQ2xhc3MiLCJlcGkudGltZS5kaXN0YW5jZS5jYXQiKV0KCiMjIyMjIyMjIyMjIwojIHNvbWUgaXNzdWVzIHdpdGggdXBkYXRlIHRvIFI0IC0gZG91YmxlIHNpZGVkIG1hdHJpeApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVt4LGMoIlRheGExIiwiVGF4YTIiKV0pKSksY29sbGFwc2U9Il9fXyIpKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSksXQoKIyBBbHNvIGhhdmluZyBhbiBpc3N1ZSB3aXRoIHRheGEgYXMgZmFjdG9ycyBoZXJlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGExIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEkVGF4YTIgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGEyKQojIyMjIyMjIyMjIyMKCiNpbnZlcnNlIHdlaWdodApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5pbnYgPC0gMS9QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5QaHlsbwoKIyBNYWtlIGFjdHVhbCBuZXR3b3JrCnNldC5zZWVkKDEyMzUpClBIRS5Ob3J0aEVhc3QubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gRikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLk5vcnRoRWFzdC5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gIkRpc3RhbmNlLmludiIpClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyRUYXhhMSA8LSBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuZ2ckdmVydGV4Lm5hbWVzCgojIGV4dHJhY3QgdGVtcG9yYWwgY2x1c3RlcnMgZnJvbSBuZXR3b3JrClBIRS5Ob3J0aEVhc3QubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuTm9ydGhFYXN0Lm5ldHdvcmspClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIGRhdGEuZnJhbWUoVGF4YTE9bmV0d29yay52ZXJ0ZXgubmFtZXMoUEhFLk5vcnRoRWFzdC5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5Ob3J0aEVhc3QubmV0d29yay5pZykpLCBjbHVzdGVyPWlncmFwaDo6Y29tcG9uZW50cyhQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuaWcpJG1lbWJlcnNoaXApCiMgRm9yIGVhc2Ugb2Ygc3RvcnkgdGVsbGluZyBpbiB0aGUgcGFwZXIsIGZsaXAgY2x1c3RlcnMgMiBhbmQgMyBhcm91bmQgKHNvIHdlIGNhbiB0YWxrIGFib3V0IDIgZmlyc3QpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlci5vbGQ9Y2x1c3RlciwgY2x1c3Rlcj1pZmVsc2UoY2x1c3Rlci5vbGQ9PTIsIDMsIGlmZWxzZShjbHVzdGVyLm9sZD09MywyLGNsdXN0ZXIub2xkKSkpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRjbHVzdGVyKQoKIyBtZXJnZSBtZXRhZGF0YSBiYWNrIGluClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgYnk9IlRheGExIiwgdHlwZT0ibGVmdCIpCgpgYGAKXApQbG90IG5ldHdvcmsKYGBge3J9CiMgUGxvdCBuZXR3b3JrCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAgPC0gZ2dwbG90KFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSkgKyAKICBnZW9tX2VkZ2VzKGFscGhhPTAuOTAsIGN1cnZhdHVyZSA9IDAuMiwgYWVzKGNvbG9yPWZhY3RvcihEaXN0YW5jZS5QaHlsbyksIGxpbmV0eXBlPWZhY3RvcihEaXN0YW5jZS5QaHlsbykpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmV5NSIsImdyZXk1NSIsImdyZXk4NSIpLCBuYW1lPSJTTlBcbkRpc3RhbmNlIikgKwogIHNjYWxlX2xpbmV0eXBlKG5hbWU9IlNOUFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgZ2VvbV9ub2RlbGFiZWwoYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiwgbGFiZWw9cGFzdGUoVGF4YTEseWVhcixzZXA9IlxuIiksZm9udGZhY2UgPSAiYm9sZCIpLCBhbHBoYT0wLjgsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbi0wLjQsIGxhYmVsLnNpemU9MC4xNSwgbGFiZWwucGFkZGluZyA9IHVuaXQoMC4wNSwgImxpbmVzIikpICsKICBnZW9tX25vZGVzKHNpemU9MS4wLCBhZXMoY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAKCmBgYAoKClwKT2ssIHNvIHRocmVlIG5ldHdvcmtzLiBDbGVhciBkaWZmZXJlbnRpYXRpb24gb2YgYSBoZXRlcm9zZXh1YWwgbmV0d29yayAod2l0aCAwLXNucCBkaXN0YW5jZXMpIGFuZCB0d28gcHJlZG9taW5hbnRseSBNU00gbmV0d29ya3MKXApMZXQncyBsb29rIGF0IHRoZSBwaHlsb2dlbmV0aWMgY29udGV4dCBvZiB0aG9zZSBOb3J0aCBFYXN0IGNsdXN0ZXJzIHdlJ3ZlIGRlZmluZWQuClB1bGwgb3V0IHN1YnRyZWVzIChmcm9tIHN1YmxpbmVhZ2UgMSBzdWJ0cmVlKQpcCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiMgQ2x1c3RlciAxCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEgPC0gZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50c1tQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyPT0iQ2x1c3RlcjEiLCJUYXhhMSJdKQpCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gdHJlZV9zdWJzZXQoZnVsbC5iZWFzdDIudHJlZSwgbm9kZT1CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLCBsZXZlbHNfYmFjaz0wKQoKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBpbml0aWFsLnRyYWNrLm9mZnNldCA9IDEwKQoKIyBDYW4ndCBmaXQgaW4gdGlwIGxhYnMsIGJ1dCBzaW5jZSB0aGlzIGlzIGEgcG9seXBoeWxldGljIHN1YnRyZWUsIGl0IHdvdWxkIGJlIGhlbHBmdWwgdG8gYWRkIGEgdHJhY2sgdG8gaGlnaGxpZ2h0IHRoZSBORSBzdHJhaW5zClBIRS5tZXRhZGF0YS5saW5rZWQkaXMuTm9ydGhFYXN0IDwtIGlmZWxzZShQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiTm9ydGggRWFzdCIsICJPdGhlciBFbmdsYW5kIikKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUsIGBOb3J0aCBFYXN0YD1QSEUubWV0YWRhdGEubGlua2VkJGlzLk5vcnRoRWFzdCksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTEwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkVuZ2xhbmQiLCB2YWx1ZXM9YygiI0E2Q0VFMyIsImdyZXk5NSIpLCBicmVha3M9YygiTm9ydGggRWFzdCIsIk90aGVyIEVuZ2xhbmQiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIEp1c3QgY29uZmlybSB0aGUgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIChtYWtlIHN1cmUgaXQgZG9lc24ndCBlbmNsb3NlIG90aGVyIGNsdXN0ZXJzKQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MTArKDQqNiksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuQ2x1c3RlciIsIHZhbHVlcz1jKCIjN2ZjOTdmIiwiI2JlYWVkNCIsIiNmZGMwODYiKSwgYnJlYWtzPWMoIkNsdXN0ZXIxIiwiQ2x1c3RlcjIiLCJDbHVzdGVyMyIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHNbMl0rNCksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkbGFiZWwpKS8xNSksbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkrMikpICsKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDbHVzdGVyIDIKQmVhc3QudHJlZS5ORS5jbHVzdGVyMiA8LSBnZXRNUkNBKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzW1BIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXI9PSJDbHVzdGVyMiIsIlRheGExIl0pCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSB0cmVlX3N1YnNldChmdWxsLmJlYXN0Mi50cmVlLCBub2RlPUJlYXN0LnRyZWUuTkUuY2x1c3RlcjIsIGxldmVsc19iYWNrPTEpCgpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIGluaXRpYWwudHJhY2sub2Zmc2V0ID0gMjApICsgZ2VvbV90aXBsYWIoc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBhbGlnbj1ULCBvZmZzZXQ9NSwgbGluZXNpemU9MC40KQojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTIwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKIyBhZGQgYSBiaXQgbW9yZSByb29tIHRvIHRoZSB4IGF4aXMKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIDwtIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeD1jKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMV0scC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0c1syXSsxMiksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKS8yMCktMSxsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKSswLjUpKSArIAogIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuNSwwLDAsMCwgdW5pdD0ibW0iKSkKCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENsdXN0ZXIgMwpCZWFzdC50cmVlLk5FLmNsdXN0ZXIzIDwtIGdldE1SQ0EoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHNbUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3Rlcj09IkNsdXN0ZXIzIiwiVGF4YTEiXSkKQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHRyZWVfc3Vic2V0KGZ1bGwuYmVhc3QyLnRyZWUsIG5vZGU9QmVhc3QudHJlZS5ORS5jbHVzdGVyMywgbGV2ZWxzX2JhY2s9MSkKCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgaW5pdGlhbC50cmFjay5vZmZzZXQgPSAyNikgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGFsaWduPVQsIG9mZnNldD0zLCBsaW5lc2l6ZT0wLjQpCgojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTI2Kyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZS54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMl0rMTIpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkvMjApLTEsbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkrMC41KSkgKyAKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZQoKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklECiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSAKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIApgYGAKClwKU2luY2UgQ2x1c3RlciAxIGlzIHJlYWxseSBxdWl0ZSBwb2x5cGh5bGV0aWMsIGl0IG1heWJlIG1vcmUgdXNlZnVsIHRvIHNob3cgdGhlIGNsdXN0ZXJzIGluIGNvbnRleHQgZm9yIHRoYXQgb25lCgpgYGB7cn0KIyBBZGQgTm9ydGggRWFzdCBpZGVudGlmaWVyIGNvbHVtbgpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBnaGVhdG1hcChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwLCBkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBgTm9ydGggRWFzdGA9UEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5Ob3J0aEVhc3QpLCBjb2xvcj1OVUxMLHdpZHRoPSgxL21heChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwJGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjUpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5FbmdsYW5kIiwgdmFsdWVzPWMoIiNBNkNFRTMiLCJncmV5OTUiKSwgYnJlYWtzPWMoIk5vcnRoIEVhc3QiLCJPdGhlciBFbmdsYW5kIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQoKIyBKdXN0IGNvbmZpcm0gdGhlIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSAobWFrZSBzdXJlIGl0IGRvZXNuJ3QgZW5jbG9zZSBvdGhlciBjbHVzdGVycykKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjYpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5DbHVzdGVyIiwgdmFsdWVzPWMoIiM3ZmM5N2YiLCIjYmVhZWQ0IiwiI2ZkYzA4NiIpLCBicmVha3M9YygiQ2x1c3RlcjEiLCJDbHVzdGVyMiIsIkNsdXN0ZXIzIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA2LCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIGFkZCBhIGJpdCBtb3JlIHJvb20gdG8gdGhlIHggYXhpcwpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzIDwtIGdncGxvdF9idWlsZChwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCkkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kcmFuZ2UkcmFuZ2UKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih4PWMocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQueC5heGlzLmxpbWl0c1sxXSxwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzJdKzQpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkvMTUpLGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQkZGF0YSRsYWJlbCkpKzIpKQoKIyByZWR1Y2Ugc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZCBzY2FsZXMKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjk1LDAsMCwwLCB1bml0PSJtbSIpKQpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodAoKYGBgCgoKXCAKUGxvdCB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMSA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBuY29sPTEsIGxhYmVscz1jKCJDIC0gQ2x1c3RlciAyIiwgIkQgLSBDbHVzdGVyIDMiKSwgdmp1c3Q9MS4wLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1KQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMiA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQud2l0aF9jbHVzdGVySUQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDMsMiksIGxhYmVscz1jKCJCIC0gQ2x1c3RlciAxIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkyCgoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMyA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDgsNyksIGxhYmVscz1jKCJCIC0gU3VibGluZWFnZSAxIChBbGwpIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1LCB2anVzdD0xLjApCgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkzCgpgYGAKClwKXApMb29rIG1vcmUgY2xvc2VseSBhdCBwb3B1bGF0aW9uIGRlbW9ncmFwaGljcyBvZiB0aGVzZSBjbHVzdGVycwpgYGB7cn0KIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDIKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDMKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBDb3VudHJ5IGluZm8gb24gTkUgY2x1c3RlciAzClRQQS5tZXRhMi4xICU+JSAKICBkcGx5cjo6ZmlsdGVyKFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlb19Db3VudHJ5KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkKCiMgU2VwYXJhdGUgbWV0YWRhdGEgcmVjb3JkcyBzaG93IEh1bmdhcmlhbiBzYW1wbGUgIlRQQV9IVU4xODAwMDEiIGNhbWUgZnJvbSBhIG1hbGUgYmlzZXh1YWwgKE1TV00pLgpgYGAKXApFeGFtaW5lIFNOUCBzY2FsZWQgdHJlZSBmb3IgZGlzdGFuY2VzCmBgYHtyfQoKIyBFeHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IFNOUCBkaXN0YW5jZXMKVFBBLk5FY2x1c3RlcjMucHlqYXJ0cmVlLm1yY2EgPC0gZ2V0TVJDQShUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChUUEEubWV0YTIuMVtUUEEubWV0YTIuMSRTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwsIlNhbXBsZV9OYW1lIl0pKSkKCgpUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSA8LSB0cmVlX3N1YnNldChUUEEucHlqYXIudHJlZSwgbm9kZT1UUEEuTkVjbHVzdGVyMy5weWphcnRyZWUubXJjYSwgbGV2ZWxzX2JhY2s9MSkKCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkkZGF0YQpgYGAKClwKXApEbyBzb21lIGFuYWx5c2lzIG9mIG5lYXJlc3QgbmVpZ2hib3VyIGFuZCBkaXN0YW5jZXMgdG8gTVJDQXMKYGBge3J9CmNhbGN1bGF0ZS55ZWFycy5mcm9tLm1yY2EgPC0gZnVuY3Rpb24oY3VycmVudC5nZ3RyZWUucGh5bG8sIGN1cnJlbnQuZ2d0cmVlLmRhdGEpewogICNjdXJyZW50LmdndHJlZSA8LSBCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUKICBhbGwudGlwcyA8LSBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwKICBkaXN0LjIubXJjYSA8LSBOVUxMCiAgIyMjIHB1dCBkYXRlcyBpbnRvIGRmCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLm1lZGlhbiA8LSAyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4KICBjdXJyZW50LmdndHJlZS5kYXRhJHllYXIgPC0gYXMubnVtZXJpYyhyb3VuZCgyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4sMykpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1aGlnaCA8LSByb3VuZCgyMDE5LjUgLSBzYXBwbHkoMTpucm93KGN1cnJlbnQuZ2d0cmVlLmRhdGEpLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWModW5saXN0KGN1cnJlbnQuZ2d0cmVlLmRhdGFbeCwiaGVpZ2h0XzAuOTVfSFBEIl0pKVsxXSksIDMpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1bG93IDwtIHJvdW5kKDIwMTkuNSAtIHNhcHBseSgxOm5yb3coY3VycmVudC5nZ3RyZWUuZGF0YSksZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh1bmxpc3QoY3VycmVudC5nZ3RyZWUuZGF0YVt4LCJoZWlnaHRfMC45NV9IUEQiXSkpWzJdKSwgMykKICAjIGV4dHJhY3QgZGF0ZXMgYmV0d2VlbiBzYW1wbGUgYW5kIGl0cyBNUkNBIHVzaW5nIGxvb3AKICBmb3IgKGN1cnJlbnQubm9kZSBpbiBhbGwudGlwcykgewogICAgY3VycmVudC5wYXJlbnQgPC0gYyhtYXRjaChjdXJyZW50Lm5vZGUsY3VycmVudC5nZ3RyZWUucGh5bG8kdGlwLmxhYmVsKSwgcGhhbmdvcm46OkFuY2VzdG9ycyhjdXJyZW50LmdndHJlZS5waHlsbywgbWF0Y2goYyhjdXJyZW50Lm5vZGUpLCBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwpLCAicGFyZW50IikpCiAgICAKICAgIGN1cnJlbnQubm9kZWxpc3QgPC0gY3VycmVudC5nZ3RyZWUuZGF0YVtjdXJyZW50LmdndHJlZS5kYXRhJG5vZGUgJWluJSBjdXJyZW50LnBhcmVudCxdCiAgICBjdXJyZW50LmRpc3QuMi5tcmNhIDwtIGMoY3VycmVudC5ub2RlLCBhcy5udW1lcmljKGN1cnJlbnQubm9kZWxpc3RbMSwieWVhciJdLWN1cnJlbnQubm9kZWxpc3RbMiwieWVhciJdKSkKICAgIGRpc3QuMi5tcmNhIDwtIHJiaW5kKGRpc3QuMi5tcmNhLCBjdXJyZW50LmRpc3QuMi5tcmNhKQogIH0KICBkaXN0LjIubXJjYSA8LSBkYXRhLmZyYW1lKFNhbXBsZV9OYW1lPWFzLmNoYXJhY3RlcihkaXN0LjIubXJjYVssMV0pLCBkaXN0LnRvLm1yY2E9YXMubnVtZXJpYyhkaXN0LjIubXJjYVssMl0pLCBzdHJpbmdzQXNGYWN0b3JzPUYpCiAgcmV0dXJuKGRpc3QuMi5tcmNhKQp9CgojIyMgQWxsIHNhbXBsZXMgaW4gZ2xvYmFsIHRyZWUKZGlzdC5tcmNhLmFsbC5UUEEgPC0gY2FsY3VsYXRlLnllYXJzLmZyb20ubXJjYShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBmdWxsLmJlYXN0Mi50cmVlQGRhdGEpCgpgYGAKXApNZXJnZSBkaXN0Mk1SQ0Egd2l0aCBtZXRhZGF0YQpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EgPC0gbGVmdF9qb2luKFBIRS5tZXRhZGF0YS5saW5rZWQsIGRpc3QubXJjYS5hbGwuVFBBLCBieT0iU2FtcGxlX05hbWUiKQoKcC50aW1lMm1yY2Eub3JpZW50YXRpb24gPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoZ2VuZGVyX29yaWVudGF0aW9uLCBkaXN0LnRvLm1yY2EsIGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbikpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJHZW5kZXIgT3JpZW50YXRpb24iLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IkdlbmRlciBPcmllbnRhdGlvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKQoKcC50aW1lMm1yY2EucGhlX3JlZ2lvbiA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhwaGVfY2VudHJlLCBkaXN0LnRvLm1yY2EsIGNvbG9yPXBoZV9jZW50cmUpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKHlsaW09YygwLDQwKSkgKwogIGxhYnMoeD0iVUtIU0EgUmVnaW9uIiwgeT0iWWVhcnMgdG8gTVJDQSIsIGNvbG9yPSJVS0hTQSBSZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pCgpwLnRpbWUybXJjYS5waGVfcmVnaW9uLm9yaWVudGF0aW9uIDwtIGdncGxvdChQSEUubWV0YWRhdGEubGlua2VkLmRpc3QybXJjYSwgYWVzKHBoZV9jZW50cmUsIGRpc3QudG8ubXJjYSwgY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBnZW9tX3F1YXNpcmFuZG9tKHNpemU9MC43NSwgYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArIHRoZW1lLnRleHQuc2l6ZSArCiAgY29vcmRfZmxpcCh5bGltPWMoMCwyMCkpICsKICBsYWJzKHg9IlVLSFNBIFJlZ2lvbiIsIHk9IlllYXJzIHRvIE1SQ0EiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJHZW5kZXJcbk9yaWVudGF0aW9uIiwgdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikKcC50aW1lMm1yY2EucGhlX3JlZ2lvbi5vcmllbnRhdGlvbgoKCnAudGltZTJtcmNhLnN1YmxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IlRQQSBMaW5lYWdlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKcC50aW1lMm1yY2Euc3VibGluZWFnZQoKCnAudGltZTJtcmNhLkxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBX0xpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBX0xpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIChNZWRpYW4gb2YgUG9zdGVyaW9yKSIsIGNvbG9yPSJUUEEgTGluZWFnZSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKQpgYGAKXApcCk1heWJlIGNhbiBtYWtlIGFuIE1TVCBvZiB0aGUgTm9ydGggRWFzdCBzYW1wbGVzIGZvciBncmFwZXRyZWU/CmBgYHtyfQpUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0IDwtIGFwZTo6a2VlcC50aXAoVFBBLnB5amFyLnRyZWUsIGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiU2FtcGxlX05hbWUiXSkpKQoKI2dndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0KQojd3JpdGUudHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0LCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LU5vcnRoRWFzdC5weWphci4yMDIyLTAyLTI2LnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKCkFsdGVybmF0aXZlIGFwcHJvYWNoIHVzaW5nIE1TVCBpbnN0ZWFkIG9mIG5ldHdvcmtzIGZvciBOb3J0aCBFYXN0IGRhdGEKYGBge3J9CiMgUmVhZCBpbiBNU1QKI1RQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLU5vcnRoRWFzdC0yMDIyLTAyLTI2LkdlbmRlck9yaWVudGF0aW9uLU1TVHJlZS5pbmtzY2FwZWQuK25vZGUtY291bnRzK0dCTVNNLnN2ZyIpCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUpCnAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIgPC0gcGxvdF9ncmlkKHAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLCBsYWJlbHM9YygiQSAtIE5ldHdvcmsgQ2x1c3RlcnMgKE5vcnRoIEVhc3QgRW5nbGFuZCkiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSkKCmBgYApcClBsb3Qgd2l0aCBiZWFzdCB0cmVlcwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQojcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDMsNiksIGxhYmVscz1jKCJBIC0gTmV0d29yayBDbHVzdGVycyAoTm9ydGggRWFzdCBFbmdsYW5kKSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGUgPSAwLjk1KQoKcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTMsIG5jb2w9MSwgcmVsX2hlaWdodHM9YygzLDcpKQoKCgpwLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmkKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzNfU3VibGluMS5Ob3J0aEVhc3QuTVNUK0JlYXN0LiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTIwMCwgaGVpZ2h0PTI0NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCgpgYGAKClwKRG8gc29tZSBhbmFseXNpcyBvZiBtYWpvciBzdWJsaW5lYWdlcyBvdmVyIHRpbWUgYnkgcmVnaW9uIC0gY291bGQgdGhpcyBpbmZsdWVuY2Ugb2JzZXJ2YXRpb25zIGFib3V0IHN1YmxpbmVhZ2VzPwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NH0KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihUUEEucGluZWNvbmUuc3VibGluZWFnZSAlaW4lIGMoMSwxNCkpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgcGhlX2NlbnRyZSwgeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MocGhlX2NlbnRyZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKCmdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIHBoZV9jZW50cmUsIHNpemU9Q291bnQsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoKSArIAogIGZhY2V0X2dyaWQoLn5UUEEucGluZWNvbmUuc3VibGluZWFnZSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCgoKcC5QSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZS5idWJibGVwbG90IDwtIGdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBjb2xvcj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGZhY2V0X2dyaWQoZmFjdG9yKGdzdWIoIlxcICIsIlxuIixwaGVfY2VudHJlKSwgbGV2ZWxzPWdzdWIoIlxcICIsIlxuIixQSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pKX4uLCBzd2l0Y2g9J3knKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj0nd2hpdGUnLCBmaWxsPSd3aGl0ZScsbGluZXR5cGU9InNvbGlkIiksIHN0cmlwLnRleHQueT1lbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1IixhbmdsZT0wLCBzaXplPTUpKSArIAogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDQuNSxicmVha3M9YygxLDUsMTAsMjAsMzAsNDApKSArCiAgdGhlbWUudGV4dC5zaXplICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKyAKICBsYWJzKHk9IlJlZ2lvbiIsIHg9IlllYXIiLCBjb2xvcj0iU3VibGluZWFnZSIpIAogCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUuYnViYmxlcGxvdAoKYGBgClwKRG8gc29tZSBzcGVjaWZpYyBhbmFseXNpcyBmb3IgdGhlIDMgTm9ydGhlcm4gcmVnaW9ucwpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9M30KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkKCiBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuM05vcnRoZXJuUmVnaW9ucyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB5ZWFyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLCBDb3VudCwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoPTAuNjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIHRoZW1lX2J3KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDIwMTIsMjAxOCwxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9cHJldHR5KSArCiAgbGFicyh0aXRsZT0iU2FtcGxlcyBpbiAzIE5vcnRoZXJuIFJlZ2lvbnMiLCB4PSJDb2xsZWN0aW9uIFllYXIiLCB5PSJTYW1wbGUgQ291bnQiLCBmaWxsPSJQdWJsaWMgSGVhbHRoXG5SZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAjZ2VvbV90ZXh0KGFlcyh4PXllYXIseT1Db3VudC0wLjUsIGxhYmVsPUNvdW50KSwgY29sb3I9J2dyZXk5NScsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogIE5VTEwKcC5QSEUubWFqb3Iuc3VibGluZWFnZS4zTm9ydGhlcm5SZWdpb25zCgpgYGAKCgoKXApTaW5nbGUgbGlua2FnZSBuZXR3b3JrIG9mIGlkZW50aWNhbCBnZW5vbWVzIGZyb20gVUsKCmBgYHtyfQojIENvbnN0cmFpbiBieSBTTlAgZGlzdGFuY2UgKGlkZW50aWNhbCBpbiB0aGUgYXNyIHNucCB0cmVlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLF0KCiMgYW5kIGEgbWF4IG9mIDIgeWVhcnMKI1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzW1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MixdCgoKIyBBbmQgbWFrZSBzdXJlIHRoYXQgd2UgYWN0dWFsbHkgaGF2ZSBnZW5ldGljIGRpc3RhbmNlIGRhdGEgZm9yIGFsbCBzYW1wbGVzIHdpdGhpbiB0aGUgbmV0d29yawpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMkRGlzdGFuY2UuUGh5bG8pLF0KCiMgcmVtb3ZlIHNlbGYtc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyRzYW1lLnNhbXBsZT09ImRpZmZlcmVudCIsXQoKCiMgY2xlYW51cCBzb21lIGRhdGEgbm9pc2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJHllYXIudDEpLF0KCiMgcHJlcGFyZSBpbnRwdXQgZGF0YSAod2l0aCBlZGdlIGluZm8pClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1ssYygiVGF4YTEiLCJUYXhhMiIsIkRpc3RhbmNlLlBoeWxvIiwiZGVjaW1hbC5kYXRlLmRpc3RhbmNlIiwieWVhci5kaXN0YW5jZSIsIk9yaWVudGF0aW9uLkNsYXNzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0LnllYXJzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0IildCgojIyMjIyMjIyMjIyMKIyBzb21lIGlzc3VlcyB3aXRoIHVwZGF0ZSB0byBSNCAtIGRvdWJsZSBzaWRlZCBtYXRyaXgKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGVkZ2VuYW1lIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDFbeCxjKCJUYXhhMSIsIlRheGEyIildKSkpLGNvbGxhcHNlPSJfX18iKSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZWRnZW5hbWUpLF0KCiMgQWxzbyBoYXZpbmcgYW4gaXNzdWUgd2l0aCB0YXhhIGFzIGZhY3RvcnMgaGVyZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTEgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJFRheGEyIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTIpCiMjIyMjIyMjIyMjIwojIERlZHVwbGljYXRlCgojaW52ZXJzZSB3ZWlnaHQKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYgPC0gMS8xLyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlKzAuMDQpCgojIE1ha2UgYWN0dWFsIG5ldHdvcmsKc2V0LnNlZWQoMTIzNikKUEhFLmlkZW50aWNhbHMubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IEYsIGxvb3BzID0gRikKCiNQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIGdnbmV0d29yayhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYiKQojUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLmlkZW50aWNhbHMubmV0d29yaywgbGF5b3V0ID0gImZydWNodGVybWFucmVpbmdvbGQiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZSIpClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gZ2duZXR3b3JrKFBIRS5pZGVudGljYWxzLm5ldHdvcmssIGxheW91dCA9ICJmcnVjaHRlcm1hbnJlaW5nb2xkIikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckVGF4YTEgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyR2ZXJ0ZXgubmFtZXMKCiMgZXh0cmFjdCB0ZW1wb3JhbCBjbHVzdGVycyBmcm9tIG5ldHdvcmsKUEhFLmlkZW50aWNhbHMubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMgPC0gZGF0YS5mcmFtZShUYXhhMT1uZXR3b3JrLnZlcnRleC5uYW1lcyhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuaWcpKSwgY2x1c3Rlcj1pZ3JhcGg6OmNvbXBvbmVudHMoUEhFLmlkZW50aWNhbHMubmV0d29yay5pZykkbWVtYmVyc2hpcCkKUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkY2x1c3RlcikKCiMgbWVyZ2UgbWV0YWRhdGEgYmFjayBpbgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gcGx5cjo6am9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBkYXRhLmZyYW1lKFRheGExPVBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKQoKCiMgCiMgQWRkIHRlbXBvcmFsIGNvbG91ciBzY2FsZQojdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckZXBpLnRpbWUuZGlzdGFuY2UuY2F0KQoKZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMgPC0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiR3JleXMiKSkobGVuZ3RoKHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJGVwaS50aW1lLmRpc3RhbmNlLmNhdCkpLTEpKQoKCiMgUGxvdCBuZXR3b3JrCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QIDwtIGdncGxvdChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpKSArIAogIGdlb21fZWRnZXMoYWxwaGE9MC45MCwgY3VydmF0dXJlID0gMC4yLCBhZXMoY29sb3I9ZmFjdG9yKGVwaS50aW1lLmRpc3RhbmNlLmNhdCksIGxpbmV0eXBlPWZhY3RvcihlcGkudGltZS5kaXN0YW5jZS5jYXQpKSkgKwogICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyZXk1IiwiZ3JleTM1IiwiZ3JleTU1IiwgImdyZXk2NSIsICJncmV5NzUiKSwgbmFtZT0iU05QXG5EaXN0YW5jZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVGVtcG9yYWxcbkRpc3RhbmNlIiwgdmFsdWVzID0gZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMpICsKICBzY2FsZV9saW5ldHlwZShuYW1lPSJUZW1wb3JhbFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgI2dlb21fbm9kZWxhYmVsKGFlcyhjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24sIGxhYmVsPXBhc3RlKFRheGExLHllYXIsc2VwPSJcbiIpLGZvbnRmYWNlID0gImJvbGQiKSwgYWxwaGE9MC44LCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4tMC40LCBsYWJlbC5zaXplPTAuMTUsIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuMDUsICJsaW5lcyIpKSArCiAgZ2VvbV9ub2RlcyhzaXplPTIuNSwgYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiksIGFscGhhPTAuOSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpgYGAKClBsb3QgdGhpcyBhZ2FpbnN0IGEgVUsgdHJlZT8KYGBge3J9CmdoZWF0bWFwKGdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspLApkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXI9UEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpKQoKYGBgCgoKClwKU29tZSBzdGF0cyBmcm9tIHRoaXMKYGBge3J9CnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieWRhdGVkaXN0IDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiwgRGlzdGFuY2UuUGh5bG89PTApICU+JQogICNmaWx0ZXIoZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuZGF0ZT1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5kYXRlKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuZGF0ZS9zdW0uY2xhc3MpICU+JQogIGdncGxvdChhZXMoeD1lcGkudGltZS5kaXN0YW5jZS5jYXQsIHk9Q291bnQuY2xhc3MuZGF0ZSwgZmlsbD1PcmllbnRhdGlvbi5DbGFzcykpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdzdGFjaycpICsKICB0aGVtZV9idygpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iVGltZSBiZXR3ZWVuIHNhbXBsZXMiLCB5PSJJbnRlcmFjdGlvbiBDb3VudCIsIGZpbGw9Ik9yaWVudGF0aW9uIFR5cGUiKQpwLlBIRS5pZGVudGljYWwuT3JpZW50YXRpb25fY2xhc3MuYnlkYXRlZGlzdAoKCiAgCnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShPcmllbnRhdGlvbi5DbGFzcykpICU+JQogIGRwbHlyOjpncm91cF9ieShDbHVzdGVyLCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuY2x1c3Rlcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5jbHVzdGVyKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuY2x1c3Rlci9zdW0uY2xhc3MpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2Moc3VtLmNsYXNzKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKENsdXN0ZXI9YXNfZmFjdG9yKENsdXN0ZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Q2x1c3RlciwgeT1Db3VudC5jbGFzcy5jbHVzdGVyLCBmaWxsPU9yaWVudGF0aW9uLkNsYXNzKSkgKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgcG9zaXRpb249J3N0YWNrJykgKyAKICB0aGVtZV9idygpICsKICB4LnRoZW1lLmF4aXMucm90YXRlICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iSW50ZXJhY3Rpb24gQ291bnQiLCBmaWxsPSJPcmllbnRhdGlvbiBUeXBlIikKcC5QSEUuaWRlbnRpY2FsLk9yaWVudGF0aW9uX2NsYXNzLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgQ2x1c3RlciwgZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50Lm9yaWVudC5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5vcmllbnQuY2x1c3RlciksIGZyYWN0PWNvdW50Lm9yaWVudC5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCiMgUGxvdCBzYW1wbGUgY291bnRzIGJ5IGdlbm9tZSBjbHVzdGVyIChjb2xvdXJlZCBieSBvcmllbnRhdGlvbikKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5vcmllbnQuY2x1c3RlciwgZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC42NSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgeC50aGVtZS5heGlzLnJvdGF0ZSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsNDUsNSkpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iU2FtcGxlIENvdW50IiwgZmlsbD0iUGF0aWVudCBHZW5kZXIgT3JpZW50YXRpb24iKSAKCiMgQWRkIGRldGFpbHMgb2Ygc3VibGluZWFnZSAgCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciArIAogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGE9KGQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIHNlbGVjdChDbHVzdGVyLm8sIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUgZGlzdGluY3QoKSksIGFlcyhDbHVzdGVyLm8sIC0xLjUsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgaW5oZXJpdC5hZXMgPSBGKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBuYW1lPSJTdWJsaW5lYWdlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIE5VTEwKCiMgQWRkIGEgc3VibGluZWFnZSBheGlzIGxhYmVsIChiaXQgb2YgYSBoYWNrKQpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZW9tX3RleHQoZGF0YT1kYXRhLmZyYW1lKGxhYj0iU3VibGluZWFnZSIsIHk9LTEuNSwgeD0yOCwgc3RyaW5nc0FzRmFjdG9ycz1GKSwgYWVzKGxhYmVsPWxhYiwgeD14LCB5PXkpLCBoanVzdCA9IDAuMSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBjb29yZF9jYXJ0ZXNpYW4oeD1jKDEsIDI3KSwgY2xpcD0nb2ZmJykKICAKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKIyMjIyMjZ3h4eHhnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKXApQb3NzaWJsZSB0byBpbnRyb2R1Y2Ugc29tZSBtb3JlIGluZm8gaW50byB0aGF0IHBsb3Q/CgoKYGBge3J9CmQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGxlZnRfam9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHNbLGMoIlRheGExIiwiQ2x1c3RlciIpXSwgUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIGJ5PWMoIlRheGExIj0iU2FtcGxlX05hbWUiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBDbHVzdGVyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnJlZ2lvbi5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5yZWdpb24uY2x1c3RlciksIGZyYWN0PWNvdW50LnJlZ2lvbi5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCnAuUEhFLmlkZW50aWNhbC5SZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5yZWdpb24uY2x1c3RlciwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTAuNjUsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHgudGhlbWUuYXhpcy5yb3RhdGUgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDQ1LDUpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iUmVnaW9uIFByb3BvcnRpb24iLCBmaWxsPSJVS0hTQSBSZWdpb24iKSAKCgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwLlBIRS5pZGVudGljYWwuYnlaZXJvZGlzdC5jbHVzdGVyLmJhcmNvbWJpIDwtIHBsb3RfZ3JpZChwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgeC50aGVtZS5zdHJpcCwgcC5QSEUuaWRlbnRpY2FsLlJlZ2lvbi5ieVplcm9kaXN0LmNsdXN0ZXIsIG5jb2w9MSwgYXhpcz0icmx0IiwgYWxpZ249VCwgcmVsX2hlaWdodHMgPSBjKDIsMSksIGxhYmVscz1jKCJCIiwiQyIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKI3AuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkKI3AuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpwbG90X2dyaWQocC5QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLjBTTlAsIHAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmksIG5jb2w9MSwgcmVsX2hlaWdodHM9YygyLDMpLCBsYWJlbHM9YygiQSIsIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKCnAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkubm9OZXQgPC0gcGxvdF9ncmlkKHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyB4LnRoZW1lLnN0cmlwLCBwLlBIRS5pZGVudGljYWwuUmVnaW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciwgbmNvbD0xLCBheGlzPSJybHQiLCBhbGlnbj1ULCByZWxfaGVpZ2h0cyA9IGMoMiwxKSwgbGFiZWxzPWMoIkEiLCJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCnAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkubm9OZXQgCgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMjAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKCgpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5yZWdpb24uc2NhdHRlcnBpZS5ncm91cHMgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6c2VsZWN0KENsdXN0ZXIsIFRheGExLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3RlciwgcGhlX2NlbnRyZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2VudHJlPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh4PUNsdXN0ZXIsIHk9My41KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPSJwaGVfY2VudHJlIiwgdmFsdWVzX2Zyb209IkNvdW50LmNlbnRyZSIsIHZhbHVlc19maWxsPTApICU+JQogIGRwbHlyOjpzZWxlY3QoQ2x1c3Rlcix4LHksdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckcGhlX2NlbnRyZSkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm51bWVyaWM9YXMubnVtZXJpYygxOjI3KSkKICAKCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpICMrCiAgCgpgYGAKCgoKXApHZXQgYSBmZXcgbW9yZSBzdGF0cyBvbiB0aGUgbGFyZ2VzdCBjbHVzdGVyIChDbHVzdGVyIDgpCmBgYHtyfQojZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciAlPiUgZmlsdGVyKENsdXN0ZXI9PSJDbHVzdGVyOCIpCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbC5jbHVzdGVyOCA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3Rlcj09IkNsdXN0ZXI4IikgICU+JQogIHNlbGVjdCh2ZXJ0ZXgubmFtZXMsIE9yaWVudGF0aW9uLkNsYXNzLCBwaGVfY2VudHJlLCB5ZWFyLCBUUEFfTGluZWFnZSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGhpdnBvcywgQ2x1c3RlcikKCnNvcnQodW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsLmNsdXN0ZXI4JHllYXIpKQoKYGBgCgpcCkdldCBzb21lIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGhldGVyb3NleHVhbCBvbmx5IGNsdXN0ZXJzCmBgYHtyfQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9oZXRlcm9jbHVzdGVycyA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3RlciAlaW4lIGMoIkNsdXN0ZXIxMiIsICJDbHVzdGVyMjAiLCAiQ2x1c3RlcjI3IikpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBDbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIHBoZV9jZW50cmUsIHllYXIsIFRQQV9MaW5lYWdlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKENsdXN0ZXIsIHllYXIsIGdlbmRlcl9vcmllbnRhdGlvbikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsX2hldGVyb2NsdXN0ZXJzCmBgYApcIApBbmQgZG8gdGhlIHNhbWUgZm9yIHRoZSBzbWFsbCBtaXhlZC9HQk1TTSBjbHVzdGVycwpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfbm90LmhldGVyb2NsdXN0ZXJzIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgJT4lIGZpbHRlcihDbHVzdGVyICVub3RpbiUgYygiQ2x1c3RlcjEyIiwgIkNsdXN0ZXIyMCIsICJDbHVzdGVyMjciLCAiQ2x1c3RlcjgiKSkgICU+JQogIHNlbGVjdCh2ZXJ0ZXgubmFtZXMsIENsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbiwgcGhlX2NlbnRyZSwgeWVhciwgVFBBX0xpbmVhZ2UsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBoaXZwb3MpICU+JSAKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoQ2x1c3RlciwgeWVhciwgZ2VuZGVyX29yaWVudGF0aW9uKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9ub3QuaGV0ZXJvY2x1c3RlcnMKYGBgCgoKCldoYXQgcHJvcG9ydGlvbiBvZiBoZXRlcm9zZXh1YWxzIGhhdmUgYW4gaWRlbnRpY2FsIEdCTVNNIHBhaXJlZCBnZW5vbWU/ClwKYGBge3J9CgojIERlbGluZWF0ZSBoZXRlcm9zZXh1YWwgY2x1c3RlcnMKZC5QSEUuaWRlbnRpY2FsLmhldGVyb3NleHVhbC5jbHVzdGVycyA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JSAKICBkcGx5cjo6bXV0YXRlKGlzLmhldGVyb3NleHVhbD1pZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uJWluJSBjKCJNU1ciLCAiV1NNIiksICJoZXRlcm9zZXh1YWwiLCBpZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uPT0iR0JNU00iLCJHQk1TTSIsICJVbmtub3duIikpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3Rlcixpcy5oZXRlcm9zZXh1YWwpICU+JSAKICBkcGx5cjo6bXV0YXRlKGNvdW50LmhldGVybz1zdW0oY291bnQub3JpZW50LmNsdXN0ZXIpLCBmcmFjdC5oZXRlcm89c3VtKGNvdW50Lm9yaWVudC5jbHVzdGVyKS9jb3VudC5jbHVzdGVyKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjpmaWx0ZXIoaXMuaGV0ZXJvc2V4dWFsPT0iaGV0ZXJvc2V4dWFsIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWMoY291bnQub3JpZW50LmNsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbiwgZnJhY3QpKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIudHlwZT1pZmVsc2UoZnJhY3QuaGV0ZXJvPT0xLCAiaGV0ZXJvLm9ubHkiLCAib3RoZXIiKSkKCmQuUEhFLmlkZW50aWNhbC5oZXRlcm9zZXh1YWwuY2x1c3RlcnMgCgojIFdoYXQgcHJvcG9ydGlvbiBvZiBoZXRlcm9zZXh1YWxzIChuPTIwKSBhcmUgaW4gYSBoZXRlcm9zZXh1YWwtb25seSBjbHVzdGVyPwpkLlBIRS5pZGVudGljYWwuaGV0ZXJvc2V4dWFsLmNsdXN0ZXJzICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3Rlci50eXBlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LmluLmhldGVyby5jbHVzdGVyPXN1bShjb3VudC5oZXRlcm8pKSAlPiUgCiAgZHBseXI6Om11dGF0ZShmcmFjdC5pbi5oZXRlcm89Y291bnQuaW4uaGV0ZXJvLmNsdXN0ZXIvc3VtKGNvdW50LmluLmhldGVyby5jbHVzdGVyKSkKICAKCiNsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpCmBgYAoKXAoKCiMgUmV2aXNpb25zIDAzLTIwMjMgb253YXJkcwoKXApMb29rIGF0IHByb3BvcnRpb24gb2YgZ2Vub21lcyBhdCBkaWZmZXJlbnQgY292ZXJhZ2UgdGhyZXNob2xkcwpgYGB7cn0KIyBDdW11bGF0aXZlIHByb3BvcnRpb24gb2YgTiBjb3VudHMgaW4gZ2Vub21lcwpQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLlVLIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaXMuVUs9PSJVSyIpICU+JQogIGRwbHlyOjpncm91cF9ieShgUHJvcG9ydGlvbi1OXz41X21hcHBpbmcrbWFza2luZ19OaWNob2xzYCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuQ291bnQ9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5Db3VudCwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9jb3VudD1jdW1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKERhdGFzZXQ9IlVLIChuPTIzNykiKQpQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLlVLCgoKUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5BTEwgPC0gVFBBLm1ldGEyLjEgJT4lIAogIGRwbHlyOjpmaWx0ZXIoZnVsbC50ZW1wb3JhbC5hbmFseXNpcz09IlllcyIpICU+JQogIGRwbHlyOjpncm91cF9ieShgUHJvcG9ydGlvbi1OXz41X21hcHBpbmcrbWFza2luZ19OaWNob2xzYCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuQ291bnQ9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5Db3VudCwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9jb3VudD1jdW1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKERhdGFzZXQ9IkFsbCAobj01MjApIikgClBIRS5tZXRhZGF0YS5OY291bnQuY3VtbXVsYXRpdmUuQUxMClBIRS5tZXRhZGF0YS5OY291bnQuY3VtbXVsYXRpdmUuY29tYmkgPC0gcmJpbmQoUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5VSywgUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5BTEwpCgoKCgpwLmN1bXVsYXRpdmUuTmNvdW50LmZvci5kYXRzZXQgPC0gIGdncGxvdChQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLmNvbWJpICwgYWVzKGBQcm9wb3J0aW9uLU5fPjVfbWFwcGluZyttYXNraW5nX05pY2hvbHNgLCBjdW1fZnJhY3QsIGdyb3VwPURhdGFzZXQsIGNvbG9yPURhdGFzZXQpKSArIAogIGdlb21fcG9pbnQoYWxwaGE9MC43NSwgc2l6ZT0xKSArCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICd0b3AnKSArCiAgbGFicyh5PSJDdW11bGF0aXZlIGZyYWN0aW9uIG9mIGdlbm9tZXMiLCB4PSJQcm9wb3J0aW9uIG9mIHNpdGVzIG1hc2tlZCB0byBOIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMSwwLjEpKQoKcC5jdW11bGF0aXZlLk5jb3VudC5mb3IuZGF0c2V0CmBgYAoKXApCRUFTVCA5NSUgSFBEIGNhbGN1bGF0aW9ucyAocHJvdmlkZSBtb3JlIGRldGFpbHMgZm9yIDUyMCBkYXRhc2V0ICAgICkKYGBge3J9CkJFQVNULm1lZGlhbiA8LSAxLjI4ZS03CkJFQVNULjk1SFBEIDwtIGMoMS4wN2UtNywgMS40OGUtNykKU1MxNC5hbG4ubGVuZ3RoIDwtIDExMzk1NjkKCgoxLyhCRUFTVC5tZWRpYW4gKiBTUzE0LmFsbi5sZW5ndGgpCjEvKEJFQVNULjk1SFBEICogU1MxNC5hbG4ubGVuZ3RoKQpgYGAKClwKXApGdXJ0aGVyIGV2YWx1YXRpb24gb2Ygc3VibGluZWFnZSA2IChyZXZpZXdlciByZXNwb25zZSkgdXNpbmcgYW5jZXN0cmFsIHJlY29uc3RydWN0aW9uIHBlcmZvcm1lZCBvbiB0aGUgZ2xvYmFsIFRQQS1vbmx5IGFsaWdubWVudC90cmVlIHVzZWQgaW4gQmVhbGUgMjAyMS4KCmBgYHtyfQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUgPC0gcmVhZC5uZXh1cyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUuZmlsZSkKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC50cmVlLmRhdGEgPC0gZm9ydGlmeShUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUpCgpnZ3RyZWUoVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC50cmVlKSArIGdlb21fbm9kZWxhYihzaXplPTIpCgojIFJlYWQgaW4gYW5kIHByb2Nlc3MgVFBBLW9ubHkgdmNmICh0byBjb25maXJtIHNpdGVzIGFyZSB0aGUgc2FtZSkKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZiA8LSByZWFkLnZjZlIoVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZmlsZSwgdmVyYm9zZSA9IEZBTFNFKQpUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCA8LSBnZXRGSVgoVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZikKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXggPC0gZGF0YS5mcmFtZShUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeFssYygyLDQsNSldLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXgkaW4uVFBBLm9ubHkgPC0gInllcyIKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXgkS2V5IDwtIDE6bnJvdyhUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCkKCmBgYAoKXApFeHRyYWN0IGdlbm90eXBlIHNpdGVzCmBgYHtyfQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndCA8LSBleHRyYWN0X2d0X3RpZHkoVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZikKClRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmYgPC0gcGx5cjo6am9pbihUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndCwgVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXhbLGMoIktleSIsIlBPUyIpXSwgYnk9IktleSIsIHR5cGU9ImxlZnQiKQoKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZ3QuZiRQT1MgPC0gYXMubnVtZXJpYyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mJFBPUykKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZ3QuZiRndF9HVCA8LSBhcy5udW1lcmljKFRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmYkZ3RfR1QpCgpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCA8LSB0aWR5cjo6c3ByZWFkKFRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmZbLGMoIlBPUyIsIkluZGl2IiwiZ3RfR1QiKV0sIFBPUywgZ3RfR1QpIAoKYGBgCgpVc2Ugc25wRWZmIHRvIGFubm90YXRlIG11bHRpLXZjZiwgYW5kIHRoZW4gcHVsbCBpbiBhbm5vdGF0aW9ucyBoZXJlCmBgYHtyfQpUUEEuc25wRWZmIDwtIHJlYWQudGFibGUoVFBBLnNucEVmZi5maWxlLGhlYWRlciA9IFQsIGNoZWNrLm5hbWVzID0gRiwgY29tbWVudC5jaGFyID0gIiIsc2VwPSJcdCIpCgpUUEEuc25wRWZmLmZpbHQgPC0gVFBBLnNucEVmZlshKFRQQS5zbnBFZmYkYEFOTlsqXS5HRU5FYD09ImdlbmUtVFBBU1NfUlMwMDA0MCIgJiBUUEEuc25wRWZmJGBBTk5bKl0uRUZGRUNUYD09ImludHJhZ2VuaWNfdmFyaWFudCIpLF0KVFBBLnNucEVmZi5maWx0W1RQQS5zbnBFZmYuZmlsdCRgQU5OWypdLkVGRkVDVGA9PSIuIiwiQU5OWypdLkVGRkVDVCJdIDwtICJpbnRyYWdlbmljX3ZhcmlhbnQiCgoKVFBBLnNucEVmZi5maWx0ICU+JSBkcGx5cjo6Z3JvdXBfYnkoYEFOTlsqXS5FRkZFQ1RgKSAlPiUgc3VtbWFyaXNlKENvdW50PW4oKSkKVFBBLnNucEVmZi5maWx0ICU+JSBkcGx5cjo6Z3JvdXBfYnkoYEFOTlsqXS5HRU5FYCkgJT4lIHN1bW1hcmlzZShDb3VudD1uKCkpClRQQS5zbnBFZmYuZmlsdCAlPiUgZHBseXI6Omdyb3VwX2J5KGBBTk5bKl0uR0VORWAsYEFOTlsqXS5FRkZFQ1RgKSAlPiUgc3VtbWFyaXNlKENvdW50PW4oKSkKClRQQS5zbnBFZmYuZmlsdC52YXIucGVyLnBvcyA8LSBUUEEuc25wRWZmLmZpbHQgJT4lIGRwbHlyOjpncm91cF9ieShQT1MpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKQpUUEEuc25wRWZmLmZpbHQudmFyLnBlci5wb3MubXVsdGkgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIodW5saXN0KFRQQS5zbnBFZmYuZmlsdC52YXIucGVyLnBvc1tUUEEuc25wRWZmLmZpbHQudmFyLnBlci5wb3MkQ291bnQ+MSwiUE9TIl0pKSkKClRQQS5zbnBFZmYuZmlsdFtUUEEuc25wRWZmLmZpbHQkUE9TICVpbiUgVFBBLnNucEVmZi5maWx0LnZhci5wZXIucG9zLm11bHRpLF0KCmBgYAoKXApMZXRzIHB1bGwgaW4gZ2VuZSBmdW5jdGlvbiAod2hlcmUga25vd24pIGZvciB0aGVzZSBzaXRlcyBmcm9tIHRoZSBnZmYKYGBge3J9ClNTMTQuZ2ZmIDwtIGFwZTo6cmVhZC5nZmYoU1MxNC5nZmYuZmlsZSkKU1MxNC5nZmYuY2RzIDwtIFNTMTQuZ2ZmW1NTMTQuZ2ZmJHR5cGU9PSJDRFMiLF0KCiMjIyMgZnVuY3Rpb24gdG8gZXh0cmFjdCBkaWZmZXJlbnQgZmllbGRzIGZyb20gYXR0cmlidXRlcyBjb2x1bW4KZ2V0QXR0cmlidXRlRmllbGQgPC0gZnVuY3Rpb24gKHgsIGZpZWxkLCBhdHRyc2VwID0gIjsiKSB7CiAgICAgcyA9IHN0cnNwbGl0KHgsIHNwbGl0ID0gYXR0cnNlcCwgZml4ZWQgPSBUUlVFKQogICAgIHNhcHBseShzLCBmdW5jdGlvbihhdHRzKSB7CiAgICAgICAgIGEgPSBzdHJzcGxpdChhdHRzLCBzcGxpdCA9ICI9IiwgZml4ZWQgPSBUUlVFKQogICAgICAgICBtID0gbWF0Y2goZmllbGQsIHNhcHBseShhLCAiWyIsIDEpKQogICAgICAgICBpZiAoIWlzLm5hKG0pKSB7CiAgICAgICAgICAgICBydiA9IGFbW21dXVsyXQogICAgICAgICB9CiAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgcnYgPSBhcy5jaGFyYWN0ZXIoTkEpCiAgICAgICAgIH0KICAgICAgICAgcmV0dXJuKHJ2KQogICAgIH0pCn0KIyMjCiNnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgIk5hbWUiKQoKCiMgRXh0cmFjdCBhdHRyaWJ1dGUgZWxlbWVudHMgZnJvbSBnZmYKU1MxNC5nZmYuY2RzJGdlbmVpZCA8LSBnc3ViKCJnZW5lXFwtIiwiIixnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgIlBhcmVudCIpKQpTUzE0LmdmZi5jZHMkbG9jdXNfdGFnIDwtIGdldEF0dHJpYnV0ZUZpZWxkKFNTMTQuZ2ZmLmNkcyRhdHRyaWJ1dGVzLCAibG9jdXNfdGFnIikKU1MxNC5nZmYuY2RzJGdlbmUgPC0gZ2V0QXR0cmlidXRlRmllbGQoU1MxNC5nZmYuY2RzJGF0dHJpYnV0ZXMsICJnZW5lIikKU1MxNC5nZmYuY2RzJHByb2R1Y3QgPC0gZ2V0QXR0cmlidXRlRmllbGQoU1MxNC5nZmYuY2RzJGF0dHJpYnV0ZXMsICJwcm9kdWN0IikKU1MxNC5nZmYuY2RzJHByb3RlaW5pZCA8LSBnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgInByb3RlaW5faWQiKQojIGNyZWF0ZSBhIG1lcmdlZCBsb2N1c190YWcvZ2VuZSB0aGUgd2F5IHNucEVmZiBkb2VzClNTMTQuZ2ZmLmNkcyRnZW5laWQgPC0gc2FwcGx5KDE6bnJvdyhTUzE0LmdmZi5jZHMpLCBmdW5jdGlvbih4KSBpZmVsc2UoaXMubmEoU1MxNC5nZmYuY2RzJGdlbmVbeF0pLFNTMTQuZ2ZmLmNkcyRsb2N1c190YWdbeF0sIFNTMTQuZ2ZmLmNkcyRnZW5lW3hdKSkKU1MxNC5nZmYuY2RzJGdlbmUuY29vcmRzIDwtIHBhc3RlMChTUzE0LmdmZi5jZHMkc3RhcnQsIjoiLFNTMTQuZ2ZmLmNkcyRlbmQpCgpTUzE0LmdmZi5jZHMKYGBgCgpcCiMgcmVhZCBpbiBzbnAgY2xhc3NpZmljYXRpb25zLCBhbmQgYXBwbHkgdG8gZGlzY3JpbWluYXRvcnkgU05QcwpcCldyaXRlIHRoaXMgYXMgYSBmdW5jdGlvbi4gVGFrZXMgNCBhcmd1bWVudHM6Ci0gZGF0YWZyYW1lIG9mIHNucHMgZm9yIGVhY2ggc2FtcGxlIGluIHdpZGUgbWF0cml4IGZvcm1hdCAoZS5nLiBUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCkKLSBsb25nZm9ybSBsaXN0IG9mIFNOUHMgYW5kIHBvc3NpYmxlIGFsbGVsZXMgKGUuZy4gVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZml4KQotIHZhcmlhbnQgYW5ub3RhdGlvbnMgZGF0YWZyYW1lIChlLmcuIFRQQS5zbnBFZmYuZmlsdCkKLSBhIHZlY3RvciBvZiB0d28gbm9kZXMgaW4gdGhlIHRyZWUgdG8gY29tcGFyZSAoZS5nLiB0dC5ub2Rlcy50by5jb21wYXJlLlNTMTQpClwKYGBge3J9CmV4dHJhY3RfYnJhbmNoX3NpdGVfYWxsZWxpY19mdW5jdGlvbnMgPC0gZnVuY3Rpb24oYWxsZWxlLm1hdHJpeC5zcHJlYWQsIHNucC50YWJsZSwgc25wLmFubm90YXRpb24udGFibGUsIG5vZGVzLmxpc3QpewogICMgZmlsdGVyIFNOUCBtYXRyaXggdG8gb25seSBpbmNsdWRlIHRoZSB0d28gbm9kZXMgb2YgaW50ZXJlc3QKICBkaXNjcmltaW5hdG9yeS5zaXRlczEgPC0gYWxsZWxlLm1hdHJpeC5zcHJlYWRbYWxsZWxlLm1hdHJpeC5zcHJlYWQkSW5kaXYgJWluJSBub2Rlcy5saXN0LF0KICBkaXNjcmltaW5hdG9yeS5zaXRlczIgPC0gdGlkeXI6OmdhdGhlcihkaXNjcmltaW5hdG9yeS5zaXRlczEsUE9TLEd0LC1JbmRpdikgJT4lIAogIHRpZHlyOjpzcHJlYWQoSW5kaXYsIEd0KQogICMgRmlsdGVyIFNOUHMgdW5kZXIgY29uc2lkZXJhdGlvbiB0byB0aG9zZSB0aGF0IGFyZSBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgdHdvIG5vZGVzCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyIDwtIGRpc2NyaW1pbmF0b3J5LnNpdGVzMlsoZGlzY3JpbWluYXRvcnkuc2l0ZXMyWywyXSE9ZGlzY3JpbWluYXRvcnkuc2l0ZXMyWywzXSksXQogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiA8LSBkaXNjcmltaW5hdG9yeS5zaXRlczJbb3JkZXIoYXMubnVtZXJpYyhkaXNjcmltaW5hdG9yeS5zaXRlczIkUE9TKSksXQogICMgbWVyZ2UgaW4gdGhlIGRldGFpbHMgYWJvdXQgYWxsZWxlcyBhdCBlYWNoIHJlbGV2YW50IFNOUCBwb3NpdGlvbgogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiA8LSBwbHlyOjpqb2luKGRpc2NyaW1pbmF0b3J5LnNpdGVzMiwgc25wLnRhYmxlLGJ5PWMoJ1BPUycpLCB0eXBlPSdsZWZ0JykKICAjIGRlYWwgd2l0aCBtdWx0aS1hbGxlbGljIHNpdGVzLCBhbmQgZGlzY3JpbWluYXRlIGJldHdlZW4gdGhlbQogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiRBTFQubXVsdGkgPC0gZGlzY3JpbWluYXRvcnkuc2l0ZXMyJEFMVAogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiRBTFQgPC0gc2FwcGx5KDE6bnJvdyhkaXNjcmltaW5hdG9yeS5zaXRlczIpLCBmdW5jdGlvbih4KSBzdHJzcGxpdChkaXNjcmltaW5hdG9yeS5zaXRlczIkQUxULm11bHRpW3hdLCIsIilbWzFdXVtzb3J0KGFzLm51bWVyaWMoKChkaXNjcmltaW5hdG9yeS5zaXRlczJbeCxjKDIsMyldKSkpKVsyXV0pCiAgIyBtZXJnZSBpbiB0aGUgYW5ub3RhdGlvbiBmb3IgdGhlIGFwcHJvcHJpYXRlIGFsbGVsZS9TTlBzCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyLnNucGVmZiA8LSBwbHlyOjpqb2luKHNucC5hbm5vdGF0aW9uLnRhYmxlWyxjKCJQT1MiLCJBTFQiLCJBTk5bKl0uQUxMRUxFIiwiQU5OWypdLkVGRkVDVCIsIkFOTlsqXS5HRU5FIiwiQU5OWypdLkhHVlNfQyIsIkFOTlsqXS5IR1ZTX1AiKV0sIGRpc2NyaW1pbmF0b3J5LnNpdGVzMlssYygiUE9TIiwiUkVGIiwiQUxUIixub2Rlcy5saXN0KV0sIHR5cGU9InJpZ2h0IiwgYnk9YygiUE9TIiwiQUxUIikpCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyLnNucGVmZltpcy5uYShkaXNjcmltaW5hdG9yeS5zaXRlczIuc25wZWZmJGBBTk5bKl0uRUZGRUNUYCksIkFOTlsqXS5FRkZFQ1QiXSA8LSAiaW50cmFnZW5pY192YXJpYW50IgogICMgcmV0dXJuIG91dHB1dAogIHJldHVybihkaXNjcmltaW5hdG9yeS5zaXRlczIuc25wZWZmKQp9CmBgYAoKXApgYGB7cn0KI3R0Lm5vZGVzLnRvLmNvbXBhcmUuU1MxNC52cy5OaWNob2xzLlRQQSA8LSBjKCJOT0RFXzAwMDAwMDUiLCJOT0RFXzAwMDAxMDMiKQoKI3R0Lm5vZGVzLnRvLmNvbXBhcmUuc3VibGluZWFnZTYudnMuTVJDQS5UUEEgPC0gYygiTk9ERV8wMDAwMDAzIiwiTk9ERV8wMDAwMDAyIikKdHQubm9kZXMudG8uY29tcGFyZS5zdWJsaW5lYWdlNi52cy5NUkNBLlRQQSA8LSBjKCJOT0RFXzAwMDAwMDEiLCJOT0RFXzAwMDAwMDIiKQoKc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEgPC0gZXh0cmFjdF9icmFuY2hfc2l0ZV9hbGxlbGljX2Z1bmN0aW9ucyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCxUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCxUUEEuc25wRWZmLmZpbHQsIHR0Lm5vZGVzLnRvLmNvbXBhcmUuc3VibGluZWFnZTYudnMuTVJDQS5UUEEpCgpzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSAlPiUgZHBseXI6Omdyb3VwX2J5KGBBTk5bKl0uRUZGRUNUYCkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKcGFzdGUwKCJBbGwgVmFyaWFudHM6ICIsIG5yb3coc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEpKQpwYXN0ZTAoIlVuaXF1ZSBTaXRlczogIiwgbGVuZ3RoKHVuaXF1ZShzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRQT1MpKSkKcGFzdGUwKCJTeW5vbnltb3VzIFZhcmlhbnRzOiAiLCBucm93KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBW3N1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGBBTk5bKl0uRUZGRUNUYD09InN5bm9ueW1vdXNfdmFyaWFudCIsXSkpCnBhc3RlMCgiTm9uLVN5bm9ueW1vdXMgVmFyaWFudHM6ICIsIG5yb3coc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEFbc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkYEFOTlsqXS5FRkZFQ1RgPT0ibWlzc2Vuc2VfdmFyaWFudCIsXSkpCnBhc3RlMCgiSW50cmFnZW5pYyBWYXJpYW50cyA6IiwgbnJvdyhzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQVtzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRgQU5OWypdLkVGRkVDVGA9PSJpbnRyYWdlbmljX3ZhcmlhbnQiLF0pKQoKCgpzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRkaXN0LmZyb20ubGFzdC52YXIgPC0gYygwLCBzYXBwbHkoMjpucm93KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBKSAsIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkUE9TW3hdKSAtIGFzLm51bWVyaWMoc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkUE9TW3gtMV0pKSkgCgptZWFuKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWVkaWFuKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWluKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWF4KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikgIAoKcC5zdWJsaW5lYWdlNi5hbmNlc3RyYWwuU05Qcy5nZW5vbWVwb3MgPC0gZ2dwbG90KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBLCBhZXMoeD1hcy5udW1lcmljKFBPUyksIHk9ZGlzdC5mcm9tLmxhc3QudmFyKSkgKyAKICBnZW9tX3BvaW50KHNpemU9MSwgYWxwaGE9MC41KSArCiAgI2dlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgYWxwaGE9MC41KSArCiAgI2dlb21fbGluZShhbHBoYT0wLjEpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLFNTMTQuYWxuLmxlbmd0aCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXByZXR0eSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgbGFicyh4PSJTUzE0IEdlbm9tZSBQb3NpdGlvbiAoTkNfMDIxNTA4LjE7IChicCkpIiwgeT0iRGlzdGFuY2Ugb2YgdmFyaWFudCBmcm9tIHByZXZpb3VzIHZhcmlhbnQgc2l0ZSAoYnApIiwgdGl0bGU9Ikdlbm9tZSBwb3NpdGlvbiBvZiBTTlBzIGRlbGluZWF0aW5nIFN1YmxpbmVhZ2UgNiBmcm9tIE1SQ0Egbm9kZSIpCiAgCnAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZ2Vub21lcG9zCgoKCnAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZGlzdC5iZXR3ZWVuLmhpc3RvIDwtIHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBICU+JQogIGdncGxvdChhZXMoeD1kaXN0LmZyb20ubGFzdC52YXIpKSArIAogIHNjYWxlX3hfbG9nMTAoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz01MCkgKyAKICB0aGVtZV9saWdodCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgbGFicyh4PSJEaXN0YW5jZSBvZiB2YXJpYW50IGZyb20gcHJldmlvdXMgdmFyaWFudCBzaXRlIChicCkiLCB5PSJDb3VudCIpICsgY29vcmRfZmxpcCgpCgpwLnN1YmxpbmVhZ2U2LmFuY2VzdHJhbC5TTlBzLmRpc3QuYmV0d2Vlbi5oaXN0bwoKcGxvdF9ncmlkKHAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZ2Vub21lcG9zLCBwLnN1YmxpbmVhZ2U2LmFuY2VzdHJhbC5TTlBzLmRpc3QuYmV0d2Vlbi5oaXN0byArIHkudGhlbWUuc3RyaXAgLCByZWxfd2lkdGhzID0gYyg4LDEpLCBhbGlnbiA9IFQpCmBgYApcClwKRG8gc29tZSBmdXJ0aGVyIGFuYWx5c2lzIG9mIHRoZSBOb3J0aCBFYXN0IHN1YmxpbmVhZ2UgZGlzdHJpYnV0aW9ucy4gV2UgaGF2ZSAzNSBzYW1wbGVzIGNvbGxlY3RlZCBmcm9tIHRoZXNlIHJlZ2lvbnMsIG9mIHdoaWNoIDE3IHdlcmUgY29sbGVjdGVkIGZyb20gMjAxNCBvbndhcmRzLiBJcyBzdWJsaW5lYWdlIDE0IG1pc3NpbmcgYnkgY2hhbmNlIChjb3VsZCB3ZSBiZSBtaXNzaW5nIGl0IHNpbXBseSBiZWNhdXNlIHdlIGhhdmVuJ3QgY29sbGVjdGVkIGVub3VnaCBzYW1wbGVzKSBvciBpcyB0aGlzIG1vcmUgbGlrZWx5IHRvIHJlZmxlY3QgdHJ1ZSB1bmV2ZW4gcmVnaW9uYWwgZGlzdHJpYnV0aW9ucz8KYGBge3J9CiMgSG93IG1hbnkgZ2Vub21lcyBmb3VuZCBpbiBOb3J0aGVybiByZWdpb25zIGJlZm9yZSBhbmQgYWZ0ZXIgZmlyc3QgZGV0ZWN0aW9uIG9mIHN1YmxpbmVhZ2UgMTQgaW4gMjAxND8KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgZHBseXI6Om11dGF0ZShiZWZvcmUyMDE0PWlmZWxzZSh5ZWFyPj0yMDE0LCIyMDE0b253YXJkcyIsICJwcmUyMDE0IikpICU+JQogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoYmVmb3JlMjAxNCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudD1uKCkpCgojIFdoYXQgYXJlIHRoZSBwcm9wb3J0aW9ucyBvZiBkaWZmZXJlbnQgc3VibGluZWFnZXMgYXJvdW5kIHRoZSBVSyBiZWZvcmUgYW5kIGFmdGVyIDIwMTQ/ClBIRS5tZXRhLnBvc3QyMDE0LnN1Ymxpbi5mcmFjcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICAjZHBseXI6OmZpbHRlcih5ZWFyPj0yMDE0KSAlPiUKICBkcGx5cjo6bXV0YXRlKGJlZm9yZTIwMTQ9aWZlbHNlKHllYXI+PTIwMTQsIjIwMTRvbndhcmRzIiwgInByZTIwMTQiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGJlZm9yZTIwMTQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5hbGw9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5hbGwpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExpbmVhZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQpQSEUubWV0YS5wb3N0MjAxNC5zdWJsaW4uZnJhY3MgCgoKIyBzaW11bGF0aW5nIHBvaXNzb24gcHJvY2VzcyByIHRvIHdvcmsgb3V0IGhvdyBtYW55IHNhbXBsZXMgd2Ugd291bGQgZXhwZWN0IGluIE5vcnRoZXJuIEVuZ2xhbmQgdW5kZXIgcG9pc3NvbiBkaXN0cmlidXRpb24KCiMgV2hhdCAlIG9mIHN1YmxpbmVhZ2UgMTQgc2FtcGxlcyBhcmUgZm91bmQgaW4gdGhlIHRvdGFsIHBvcHVsYXRpb24/CnBvc3QyMDE0LnN1YmxpbjE0LmZyZXEgPC0gUEhFLm1ldGEucG9zdDIwMTQuc3VibGluLmZyYWNzICU+JSBmaWx0ZXIoYmVmb3JlMjAxND09IjIwMTRvbndhcmRzIiwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PTE0KSAlPiUgc2VsZWN0KExpbmVhZ2UucGVyYykgJT4lIHB1bGwoKQogCgojIFNpbXVsYXRlIGFuZCBwbG90IGEgUG9pc3NvbiBkaXN0cmlidXRpb24gb2YgaG93IG1hbnkgc3VibGluZWFnZSAxNCBzYW1wbGVzIHdlIHdvdWxkIGV4cGVjdCB0byBmaW5kIGlmIHdlIHJhbmRvbWx5IHNlbGVjdGVkIDE3IHNhbXBsZXMgYXQgMjIlIApkYXRhLmZyYW1lKHJwb2lzPXJwb2lzKDEwMDAwMDAsIDE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpKSAlPiUKICBnZ3Bsb3QoYWVzKHJwb2lzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwyMCwyKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIGxhYnMoeD0iU2FtcGxlcyBGb3VuZCIsIHk9IlNpbXVsYXRpb24gQ291bnQiKQoKIyBXaGF0IGFyZSB0aGUgcXVhbnRpbGUgZGlzdHJpYnV0aW9ucyBmcm9tIHRoYXQ/CnF1YW50aWxlKHJwb2lzKDEwMDAwMDAsIDE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpLCBwcm9icz1jKDAuMDEsIDAuMDUsIDAuNSwgMC45NSwgMC45OSkpCm1lZGlhbihycG9pcygxMDAwMDAwLCAxNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkKbWVhbihycG9pcygxMDAwMDAwLCAxNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkKCiMgV2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgb2YgZmluZGluZyBubyBzYW1wbGVzIChhc3N1bWluZyB1bmlmb3JtIHVuYmlhc2VkIGNvdmVyYWdlKT8KZGF0YS5mcmFtZShuPXNlcSgwLDIwLDEpLCBkcG9pcz1zYXBwbHkoc2VxKDAsMjAsMSksIGZ1bmN0aW9uKHgpIGRwb2lzKHgsIGxhbWJkYT0xNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9biwgeT1kcG9pcykpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1wcmV0dHkpICsKICB0aGVtZV9saWdodCgpICsKICBsYWJzKHg9IlNhbXBsZXMgRm91bmQiLCB5PSJQcm9iYWJpbGl0eSIpCgpwYXN0ZSgiUHJvYmFiaWxpdHkgb2YgZmluZGluZyB6ZXJvIHNhbXBsZXMgaXMgIiwgcm91bmQoZHBvaXMoMCwgbGFtYmRhPTE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpLCA1KSkgCmBgYAoKCgo=